<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  
  <title>Hexo</title>
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
  <meta name="description">
<meta property="og:type" content="website">
<meta property="og:title" content="Hexo">
<meta property="og:url" content="http://yoursite.com/page/3/index.html">
<meta property="og:site_name" content="Hexo">
<meta property="og:description">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Hexo">
<meta name="twitter:description">
  
    <link rel="alternative" href="/atom.xml" title="Hexo" type="application/atom+xml">
  
  
    <link rel="icon" href="/favicon.png">
  
  <link href="//fonts.googleapis.com/css?family=Source+Code+Pro" rel="stylesheet" type="text/css">
  <link rel="stylesheet" href="/css/style.css" type="text/css">
  

</head>
<body>
  <div id="container">
    <div id="wrap">
      <header id="header">
  <div id="banner"></div>
  <div id="header-outer" class="outer">
    <div id="header-title" class="inner">
      <h1 id="logo-wrap">
        <a href="/" id="logo">Hexo</a>
      </h1>
      
    </div>
    <div id="header-inner" class="inner">
      <nav id="main-nav">
        <a id="main-nav-toggle" class="nav-icon"></a>
        
          <a class="main-nav-link" href="/">Home</a>
        
          <a class="main-nav-link" href="/archives">Archives</a>
        
      </nav>
      <nav id="sub-nav">
        
          <a id="nav-rss-link" class="nav-icon" href="/atom.xml" title="RSS Feed"></a>
        
        <a id="nav-search-btn" class="nav-icon" title="Search"></a>
      </nav>
      <div id="search-form-wrap">
        <form action="//google.com/search" method="get" accept-charset="UTF-8" class="search-form"><input type="search" name="q" results="0" class="search-form-input" placeholder="Search"><button type="submit" class="search-form-submit">&#xF002;</button><input type="hidden" name="sitesearch" value="http://yoursite.com"></form>
      </div>
    </div>
  </div>
</header>
      <div class="outer">
        <section id="main">
  
    <article id="post-spring/2.spring事物传播机制" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-meta">
    <a href="/2016/01/21/spring/2.spring事物传播机制/" class="article-date">
  <time datetime="2016-01-21T06:29:00.000Z" itemprop="datePublished">2016-01-21</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/categories/spring/">spring</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2016/01/21/spring/2.spring事物传播机制/">spring事务传播机制</a>
    </h1>
  

      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <h2 id="u7B80_u4ECB"><a href="#u7B80_u4ECB" class="headerlink" title="简介"></a>简介</h2><p>　　Spring 对事务控制的支持统一在 TransactionDefinition 类中描述，该类有以下几个重要的接口方法：<br>　　1. int getPropagationBehavior()：事务的传播行为<br>　　2. int getIsolationLevel()：事务的隔离级别<br>　　3. int getTimeout()：事务的过期时间<br>　　4. boolean isReadOnly()：事务的读写特性。<br>　　除了事务的传播行为外，事务的其它特性是 Spring 借助底层资源的功能来完成的，它只充当代理的角色。但是事务的传播行为却是 Spring 凭借自身的框架提供的功能，是 Spring 提供给开发者最珍贵的礼物。</p>
<h2 id="u4E8B_u52A1_u914D_u7F6E"><a href="#u4E8B_u52A1_u914D_u7F6E" class="headerlink" title="事务配置"></a>事务配置</h2><p>　　spring中支持以下两种事务声明方式：<br>　　1. 编程式事务  当系统需要明确的，细粒度的控制各个事务的边界时使用，采用@Transactional注解<br>　　2. 声明式事务  当系统对于事务的控制粒度较粗时使用，xml配置，建立在AOP的基础之上。其本质是对方法前后进行拦截，然后在目标方法开始之前创建或者加入一个事务，在执行完目标方法之后根据执行情况提交或者回滚事务。其最大优点就是<strong>不需要在业务逻辑代码中掺杂事务管理的代码，只需在配置文件中做相关的事务规则声明（或通过等价的基于标注的方式），便可以将事务规则应用到业务逻辑中</strong>。这主要得益于Spring依赖注入容器和Spring AOP的支持。其最细粒度只能作用到方法级别，无法做到像编程式事务那样可以作用到代码块级别。</p>
<h2 id="u4F20_u64AD_u7C7B_u578B"><a href="#u4F20_u64AD_u7C7B_u578B" class="headerlink" title="传播类型"></a>传播类型</h2><p>　　spring支持7种事务传播行为，确定客户端和被调用端的事务边界（说得通俗一点就是<strong>多个具有事务控制的service的相互调用时所形成的复杂的事务边界控制</strong>），下图所示为7钟事务传播机制</p>
<table>
<thead>
<tr>
<th style="text-align:left">传播行为</th>
<th style="text-align:left">含义</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left">PROPAGATION_REQUIRED</td>
<td style="text-align:left">表示当前方法必须在一个具有事务的上下文中运行，如有客户端有事务在进行，那么被调用端将在该事务中运行，否则的话重新开启一个事务。<br><strong>如果被调用端发生异常，那么调用端和被调用端事务都将回滚</strong></td>
<td>调用端和被调用端共享同一个事务</td>
</tr>
<tr>
<td style="text-align:left">PROPAGATION_SUPPORTS</td>
<td style="text-align:left">表示当前方法不必需要具有一个事务上下文，但是如果有一个事务的话，它也可以在这个事务中运行</td>
<td></td>
</tr>
<tr>
<td style="text-align:left">PROPAGATION_MANDATORY</td>
<td style="text-align:left">表示当前方法必须在一个事务中运行，如果没有事务，将抛出异常</td>
<td></td>
</tr>
<tr>
<td style="text-align:left">PROPAGATION_NESTED</td>
<td style="text-align:left">表示如果当前方法正有一个事务在运行中，则该方法应该运行在一个嵌套事务中，被嵌套的事务可以独立于被封装的事务中进行提交或者回滚。<br><strong>如果封装事务存在，并且外层事务抛出异常回滚，那么内层事务必须回滚，反之，内层事务并不影响外层事务。如果封装事务不存在，则同PROPAGATION_REQUIRED的一样</strong></td>
<td></td>
</tr>
<tr>
<td style="text-align:left">PROPAGATION_NEVER</td>
<td style="text-align:left">表示当方法务不应该在一个事务中运行，如果存在一个事务，则抛出异常</td>
<td></td>
</tr>
<tr>
<td style="text-align:left">PROPAGATION_REQUIRES_NEW</td>
<td style="text-align:left">表示当前方法必须运行在它自己的事务中。<strong>一个新的事务将启动，而且如果有一个现有的事务在运行的话，则这个方法将在运行期被挂起，直到新的事务提交或者回滚才恢复执行。</strong></td>
<td></td>
</tr>
<tr>
<td style="text-align:left">PROPAGATION_NOT_SUPPORTED</td>
<td style="text-align:left">表示该方法不应该在一个事务中运行。<strong>如果有一个事务正在运行，他将在运行期被挂起，直到这个事务提交或者回滚才恢复执行</strong></td>
</tr>
</tbody>
</table>
<p>　　Spring 默认的事务传播行为是 PROPAGATION_REQUIRED，它适合于绝大多数的情况。假设 ServiveX#methodX() 都工作在事务环境下（即都被 Spring 事务增强了），假设程序中存在如下的调用链：Service1#method1()-&gt;Service2#method2()-&gt;Service3#method3()，那么这 3 个服务类的 3 个方法通过 Spring 的事务传播机制都工作在同一个事务中。要么全部成功，要么全部失败！<br>``` xml</p>
<h2 id="u603B_u7ED3"><a href="#u603B_u7ED3" class="headerlink" title="总结"></a>总结</h2><p>　　</p>
<h2 id="u53C2_u8003_u8D44_u6599"><a href="#u53C2_u8003_u8D44_u6599" class="headerlink" title="参考资料"></a>参考资料</h2><p><a href="http://blog.csdn.net/seelye/article/details/40118089" target="_blank" rel="external">http://blog.csdn.net/seelye/article/details/40118089</a><br><a href="http://blog.csdn.net/seelye/article/details/40144817" target="_blank" rel="external">http://blog.csdn.net/seelye/article/details/40144817</a></p>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://yoursite.com/2016/01/21/spring/2.spring事物传播机制/" data-id="cijzzpv29001j1cfsmot1omfr" class="article-share-link">Share</a>
      
      
  <ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/java/">java</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/spring/">spring</a></li></ul>

    </footer>
  </div>
  
</article>


  
    <article id="post-nio/18.Mina系列四之IoService" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-meta">
    <a href="/2016/01/19/nio/18.Mina系列四之IoService/" class="article-date">
  <time datetime="2016-01-19T06:04:00.000Z" itemprop="datePublished">2016-01-19</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/categories/nio/">nio</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2016/01/19/nio/18.Mina系列四之IoService/">Mina系列四之IoService</a>
    </h1>
  

      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <h2 id="u7B80_u4ECB"><a href="#u7B80_u4ECB" class="headerlink" title="简介"></a>简介</h2><p>　　IoAcceptor 和 IoConnector 均继承自IoService，它是mina的核心组件，提供标准的I/O 服务并且管理I/O 会话Session。IoService为大部分mina服务提供了底层的API支持，IoService的职责主要包括：<br>　　1. 会话Session管理：创建和销毁会话，检测session等待等。<br>　　2. 过滤器链管理：管理过滤器链，提供多个切面的服务，并允许用户在运行时动态变更过滤器链。<br>　　3. 处理调用：当有新消息或其他session生命周期触发事件响应时，回调用户的业务代码。<br>　　4. 统计管理：更新消息发送量、字节发送量等等信息。<br>　　5. 监听器管理：提供服务各个生命周期触发时间的监听器管理，如服务有效时、服务空闲时、会话创建时等。<br>　　6. 传输管理：可在服务端客户端有效的控制数据流的传输，平衡负载。<br>　　我们先看看服务端对IoService的实现，如下图所示：<img src="/images/mina-uml-server.jpg" alt="服务端UML"><br>　　服务端对nio的封装主要涉及上图左侧的几个类：AbstractIoService、AbstractIoAcceptor、AbstractPollingIoAcceptor和NioSocketAcceptor，下面我们依次说说这几个类的关键之处！</p>
<h2 id="AbstractIoService"><a href="#AbstractIoService" class="headerlink" title="AbstractIoService"></a>AbstractIoService</h2><p>　　AbstractIoService最重要几处便是构造方法的两个参数IoSessionConfig和Executor，它们是由父类的构造函数传递过来的，后面再详细讲解。还需要注意的就是其executeWorker方法，负责向Executor中添加Runnable，这个Runnable的生成也放到后面说明！<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">AbstractIoService</span> <span class="keyword">implements</span> <span class="title">IoService</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> AtomicInteger id = <span class="keyword">new</span> AtomicInteger();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> String threadName;</span><br><span class="line">    <span class="comment">//负责处理I/O事件</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Executor executor;</span><br><span class="line">	</span><br><span class="line">    <span class="keyword">private</span> IoHandler handler;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">final</span> IoSessionConfig sessionConfig;</span><br><span class="line">    <span class="keyword">private</span> IoFilterChainBuilder filterChainBuilder = <span class="keyword">new</span> DefaultIoFilterChainBuilder();</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="title">AbstractIoService</span><span class="params">(IoSessionConfig sessionConfig, Executor executor)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.sessionConfig = sessionConfig;</span><br><span class="line">        <span class="keyword">this</span>.executor = Executors.newCachedThreadPool();</span><br><span class="line">        createdExecutor = <span class="keyword">true</span>;</span><br><span class="line">        threadName = getClass().getSimpleName() + <span class="string">'-'</span> + id.incrementAndGet();</span><br><span class="line">    &#125;</span><br><span class="line">	</span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">executeWorker</span><span class="params">(Runnable worker, String suffix)</span> </span>&#123;</span><br><span class="line">        String actualThreadName = threadName;</span><br><span class="line">        <span class="keyword">if</span> (suffix != <span class="keyword">null</span>) &#123;</span><br><span class="line">            actualThreadName = actualThreadName + <span class="string">'-'</span> + suffix;</span><br><span class="line">        &#125;</span><br><span class="line">        executor.execute(<span class="keyword">new</span> NamePreservingRunnable(worker, actualThreadName));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">initSession</span><span class="params">(IoSession session, IoFuture future, IoSessionInitializer sessionInitializer)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (sessionInitializer != <span class="keyword">null</span>) &#123;</span><br><span class="line">            sessionInitializer.initializeSession(session, future);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<h2 id="AbstractIoAcceptor"><a href="#AbstractIoAcceptor" class="headerlink" title="AbstractIoAcceptor"></a>AbstractIoAcceptor</h2><p>　　我们开发的MinaTimeServer中，调用了NioSocketAcceptor的bind方法，实际上是在AbstractIoService中声明的。这里需要注意的一个点就是：mina在设计的时候考虑了<strong>服务器可能会有多个网卡的问题</strong>，详见bind的重载方法！<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">AbstractIoAcceptor</span> <span class="keyword">extends</span> <span class="title">AbstractIoService</span> <span class="keyword">implements</span> <span class="title">IoAcceptor</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="title">AbstractIoAcceptor</span><span class="params">(IoSessionConfig sessionConfig, Executor executor)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(sessionConfig, executor);</span><br><span class="line">        defaultLocalAddresses.add(<span class="keyword">null</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">bind</span><span class="params">(SocketAddress localAddress)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">        List&lt;SocketAddress&gt; localAddresses = <span class="keyword">new</span> ArrayList&lt;SocketAddress&gt;(<span class="number">1</span>);</span><br><span class="line">        localAddresses.add(localAddress);</span><br><span class="line">        bind(localAddresses);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">bind</span><span class="params">(Iterable&lt;? extends SocketAddress&gt; localAddresses)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">        List&lt;SocketAddress&gt; localAddressesCopy = <span class="keyword">new</span> ArrayList&lt;SocketAddress&gt;();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> (SocketAddress a : localAddresses) &#123;</span><br><span class="line">            localAddressesCopy.add(a);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">synchronized</span> (bindLock) &#123;</span><br><span class="line">            Set&lt;SocketAddress&gt; addresses = bindInternal(localAddressesCopy);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">	</span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">abstract</span> Set&lt;SocketAddress&gt; <span class="title">bindInternal</span><span class="params">(List&lt;? extends SocketAddress&gt; localAddresses)</span> <span class="keyword">throws</span> Exception</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>　　可以看到最后实际执行了抽象方法bindInternal，下面我们看看AbstractPollingIoAcceptor对bindInternal方法的实现</p>
<h2 id="AbstractPollingIoAcceptor"><a href="#AbstractPollingIoAcceptor" class="headerlink" title="AbstractPollingIoAcceptor"></a>AbstractPollingIoAcceptor</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">AbstractPollingIoAcceptor</span>&lt;<span class="title">S</span> <span class="keyword">extends</span> <span class="title">AbstractIoSession</span>, <span class="title">H</span>&gt; <span class="keyword">extends</span> <span class="title">AbstractIoAcceptor</span> </span>&#123;</span><br><span class="line">    <span class="comment">//默认为SimpleIoProcessorPool</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> IoProcessor&lt;S&gt; processor;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">boolean</span> createdProcessor;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">volatile</span> <span class="keyword">boolean</span> selectable;</span><br><span class="line">    <span class="keyword">private</span> AtomicReference&lt;Acceptor&gt; acceptorRef = <span class="keyword">new</span> AtomicReference&lt;Acceptor&gt;();</span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">boolean</span> reuseAddress = <span class="keyword">false</span>;</span><br><span class="line"></span><br><span class="line">	<span class="comment">//默认传入的为NioProcessor</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="title">AbstractPollingIoAcceptor</span><span class="params">(IoSessionConfig sessionConfig, Class&lt;? extends IoProcessor&lt;S&gt;&gt; processorClass)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>(sessionConfig, <span class="keyword">null</span>, <span class="keyword">new</span> SimpleIoProcessorPool&lt;S&gt;(processorClass), <span class="keyword">true</span>, <span class="keyword">null</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="title">AbstractPollingIoAcceptor</span><span class="params">(IoSessionConfig sessionConfig, Executor executor, IoProcessor&lt;S&gt; processor,</span><br><span class="line">            <span class="keyword">boolean</span> createdProcessor, SelectorProvider selectorProvider)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(sessionConfig, executor);</span><br><span class="line">        <span class="keyword">this</span>.processor = processor;</span><br><span class="line">        <span class="keyword">this</span>.createdProcessor = createdProcessor;</span><br><span class="line">        init(selectorProvider);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title">init</span><span class="params">()</span> <span class="keyword">throws</span> Exception</span>;</span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">abstract</span> <span class="keyword">int</span> <span class="title">select</span><span class="params">()</span> <span class="keyword">throws</span> Exception</span>;</span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title">wakeup</span><span class="params">()</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">abstract</span> H <span class="title">open</span><span class="params">(SocketAddress localAddress)</span> <span class="keyword">throws</span> Exception</span>;</span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">abstract</span> S <span class="title">accept</span><span class="params">(IoProcessor&lt;S&gt; processor, H handle)</span> <span class="keyword">throws</span> Exception</span>;</span><br><span class="line"></span><br><span class="line">    <span class="annotation">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">final</span> Set&lt;SocketAddress&gt; <span class="title">bindInternal</span><span class="params">(List&lt;? extends SocketAddress&gt; localAddresses)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        startupAcceptor();</span><br><span class="line">        lock.acquire();</span><br><span class="line">        <span class="comment">// Wait a bit to give a chance to the Acceptor thread to do the select()</span></span><br><span class="line">        Thread.sleep(<span class="number">10</span>);</span><br><span class="line">        wakeup();</span><br><span class="line">        lock.release();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">startupAcceptor</span><span class="params">()</span> <span class="keyword">throws</span> InterruptedException </span>&#123;</span><br><span class="line">        Acceptor acceptor = acceptorRef.get();</span><br><span class="line">        <span class="keyword">if</span> (acceptor == <span class="keyword">null</span>) &#123;</span><br><span class="line">            lock.acquire();</span><br><span class="line">            acceptor = <span class="keyword">new</span> Acceptor();</span><br><span class="line">            <span class="keyword">if</span> (acceptorRef.compareAndSet(<span class="keyword">null</span>, acceptor)) &#123;</span><br><span class="line">                executeWorker(acceptor);</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                lock.release();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//打开ServerSocketChannel</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">int</span> <span class="title">registerHandles</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="keyword">for</span> (SocketAddress a : localAddresses) &#123;</span><br><span class="line">                    H handle = open(a);</span><br><span class="line">                    newHandles.put(localAddress(handle), handle);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            &#125; <span class="keyword">finally</span> &#123;&#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="class"><span class="keyword">class</span> <span class="title">Acceptor</span> <span class="keyword">implements</span> <span class="title">Runnable</span> </span>&#123;</span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">int</span> nHandles = <span class="number">0</span>;</span><br><span class="line">            lock.release();</span><br><span class="line">            <span class="keyword">while</span> (selectable) &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    <span class="keyword">int</span> selected = select();</span><br><span class="line">                    nHandles += registerHandles();</span><br><span class="line">                    <span class="keyword">if</span> (selected &gt; <span class="number">0</span>) &#123;</span><br><span class="line">                        processHandles(selectedHandles());</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">processHandles</span><span class="params">(Iterator&lt;H&gt; handles)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">            <span class="keyword">while</span> (handles.hasNext()) &#123;</span><br><span class="line">                H handle = handles.next();</span><br><span class="line">                handles.remove();</span><br><span class="line"></span><br><span class="line">                S session = accept(processor, handle);</span><br><span class="line">                initSession(session, <span class="keyword">null</span>, <span class="keyword">null</span>);</span><br><span class="line"></span><br><span class="line">                session.getProcessor().add(session);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>　　可以看到，bindInternal最终执行了 startupAcceptor 方法，该方法将其内部类 Acceptor 实例（一个Runnable）放到了AbstractIoService中的的Executor中执行！<br>　　Acceptor 的run方法调用了抽象方法select、registerHandles(它又调用了抽象方法open)，如果selected&gt;0，则还会调用accept方法，下面我们进入NioSocketAcceptor看看其对select、open和accept等方法的实现！</p>
<h2 id="NioSocketAcceptor"><a href="#NioSocketAcceptor" class="headerlink" title="NioSocketAcceptor"></a>NioSocketAcceptor</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">NioSocketAcceptor</span> <span class="keyword">extends</span> <span class="title">AbstractPollingIoAcceptor</span>&lt;<span class="title">NioSession</span>, <span class="title">ServerSocketChannel</span>&gt; <span class="keyword">implements</span> <span class="title">SocketAcceptor</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">volatile</span> Selector selector;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">NioSocketAcceptor</span><span class="params">(<span class="keyword">int</span> processorCount)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(<span class="keyword">new</span> DefaultSocketSessionConfig(), NioProcessor.class, processorCount);</span><br><span class="line">        ((DefaultSocketSessionConfig) getSessionConfig()).init(<span class="keyword">this</span>);</span><br><span class="line">    &#125;</span><br><span class="line">	</span><br><span class="line">    <span class="annotation">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">init</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        selector = Selector.open();</span><br><span class="line">    &#125;</span><br><span class="line">	</span><br><span class="line">    <span class="annotation">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> NioSession <span class="title">accept</span><span class="params">(IoProcessor&lt;NioSession&gt; processor, ServerSocketChannel handle)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        SocketChannel ch = handle.accept();</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> NioSocketSession(<span class="keyword">this</span>, processor, ch);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="annotation">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> ServerSocketChannel <span class="title">open</span><span class="params">(SocketAddress localAddress)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line"></span><br><span class="line">        ServerSocketChannel channel = ServerSocketChannel.open();</span><br><span class="line">        channel.configureBlocking(<span class="keyword">false</span>);</span><br><span class="line">        ServerSocket socket = channel.socket();</span><br><span class="line">        socket.setReuseAddress(isReuseAddress());</span><br><span class="line"></span><br><span class="line">        socket.bind(localAddress, getBacklog());</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ServerSocketChannel的interests为OP_ACCEPT，即监听客户端的连接事件</span></span><br><span class="line">        channel.register(selector, SelectionKey.OP_ACCEPT);</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> channel;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="annotation">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">int</span> <span class="title">select</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> selector.select();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="annotation">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">wakeup</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        selector.wakeup();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>　　可以看到，上面这些方法的实现就是对原生NIO的一个简单包装罢了，到这里我们基本上可以理解IoAcceptor对客户端连接事件的处理流程：<br>　　1. 业务代码对 NioSocketAcceptor.bind 方法的调用最终导致了AbstractPollingIoAcceptor的内部类Acceptor的执行<br>　　2. Acceptor是一个Runnable，其会在Executors中执行，而Executors则是在AbstractIoService的构造方法中初始化：executor = Executors.newCachedThreadPool()<br>　　3. Acceptor的run方法会导致NioSocketAcceptor中select、open和accept等方法的执行<br>　　4. NioSocketAcceptor内部维护了一个Selector，执行open方法时会将ServerSocketChannel注册到其中，并监听OP_ACCEPT事件<br>　　5. 当有新的客户端连接时，OP_ACCEPT事件被触发，得到一个SocketChanel，并将其和SimpleIoProcessorPool、NioSocketAcceptor一起封装到NioSocketSession中<br>　　第五点提到了SimpleIoProcessorPool，它是个什么东西呢，继续看下文！</p>
<h2 id="SimpleIoProcessorPool"><a href="#SimpleIoProcessorPool" class="headerlink" title="SimpleIoProcessorPool"></a>SimpleIoProcessorPool</h2><p>　　之前我们提到NioSocketAcceptor中的Selector只监听了ServerSocketChannel的OP_ACCEPT事件，客户端连接得到的SocketChanel封装到session返回，但是并没有注册其读写事件到selecotr中，此时我们先看看Acceptor中的processHandles方法！<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">processHandles</span><span class="params">(Iterator&lt;H&gt; handles)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">	<span class="keyword">while</span> (handles.hasNext()) &#123;</span><br><span class="line">        H handle = handles.next();</span><br><span class="line">        handles.remove();</span><br><span class="line">        </span><br><span class="line">        S session = accept(processor, handle);</span><br><span class="line">        initSession(session, <span class="keyword">null</span>, <span class="keyword">null</span>);</span><br><span class="line">        <span class="comment">//重点关注此行代码的执行</span></span><br><span class="line">        session.getProcessor().add(session);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>　　之前我们提到过session初始化时会传入SimpleIoProcessorPool，所示session.getProcessor().add(session)最终执行的是SimpleIoProcessorPool的add方法，下面我们看看SimpleIoProcessorPool的关键代码！<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SimpleIoProcessorPool</span>&lt;<span class="title">S</span> <span class="keyword">extends</span> <span class="title">AbstractIoSession</span>&gt; <span class="keyword">implements</span> <span class="title">IoProcessor</span>&lt;<span class="title">S</span>&gt; </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** 线程池的大小，默认为cpu核数+1 */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> DEFAULT_SIZE = Runtime.getRuntime().availableProcessors() + <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> AttributeKey PROCESSOR = <span class="keyword">new</span> AttributeKey(SimpleIoProcessorPool.class, <span class="string">"processor"</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> IoProcessor&lt;S&gt;[] pool;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Executor executor;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">boolean</span> createdExecutor;</span><br><span class="line">    <span class="comment">//示例代码中processorType参数默认为NioProcesor、executor为null</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">SimpleIoProcessorPool</span><span class="params">(Class&lt;? extends IoProcessor&lt;S&gt;&gt; processorType, Executor executor, <span class="keyword">int</span> size, SelectorProvider selectorProvider)</span> </span>&#123;</span><br><span class="line">        createdExecutor = (executor == <span class="keyword">null</span>);</span><br><span class="line">        <span class="keyword">if</span> (createdExecutor) &#123;</span><br><span class="line">            <span class="keyword">this</span>.executor = Executors.newCachedThreadPool();</span><br><span class="line">            ((ThreadPoolExecutor) <span class="keyword">this</span>.executor).setRejectedExecutionHandler(<span class="keyword">new</span> ThreadPoolExecutor.CallerRunsPolicy());</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">this</span>.executor = executor;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        pool = <span class="keyword">new</span> IoProcessor[size];</span><br><span class="line"></span><br><span class="line">        <span class="keyword">boolean</span> success = <span class="keyword">false</span>;</span><br><span class="line">        Constructor&lt;? extends IoProcessor&lt;S&gt;&gt; processorConstructor = <span class="keyword">null</span>;</span><br><span class="line">        <span class="keyword">boolean</span> usesExecutorArg = <span class="keyword">true</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    <span class="comment">//先初始化第一个IoProcessor</span></span><br><span class="line">                    processorConstructor = processorType.getConstructor(ExecutorService.class);</span><br><span class="line">                    pool[<span class="number">0</span>] = processorConstructor.newInstance(<span class="keyword">this</span>.executor);</span><br><span class="line">                &#125; <span class="keyword">catch</span> (NoSuchMethodException e1) &#123;</span><br><span class="line">                    <span class="keyword">try</span> &#123;</span><br><span class="line">                        <span class="comment">//如果失败则采用其他的构造函数</span></span><br><span class="line">                        processorConstructor = processorType.getConstructor(Executor.class);</span><br><span class="line">                        pool[<span class="number">0</span>] = processorConstructor.newInstance(<span class="keyword">this</span>.executor);</span><br><span class="line">                    &#125; <span class="keyword">catch</span> (NoSuchMethodException e2) &#123;</span><br><span class="line">                        <span class="comment">//最后采用默认的构造函数</span></span><br><span class="line">                        processorConstructor = processorType.getConstructor();</span><br><span class="line">                        pool[<span class="number">0</span>] = processorConstructor.newInstance();</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">catch</span> (RuntimeException re) &#123;&#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 接着初始化线程池中剩余的IoProcessor</span></span><br><span class="line">            <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>; i &lt; pool.length; i++) &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    <span class="keyword">if</span> (usesExecutorArg) &#123;</span><br><span class="line">                        pool[i] = processorConstructor.newInstance(<span class="keyword">this</span>.executor);</span><br><span class="line">                    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                        pool[i] = processorConstructor.newInstance();</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125; <span class="keyword">catch</span> (Exception e) &#123;&#125;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            success = <span class="keyword">true</span>;</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;&#125;</span><br><span class="line">    &#125;</span><br><span class="line">	</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">add</span><span class="params">(S session)</span> </span>&#123;</span><br><span class="line">        getProcessor(session).add(session);</span><br><span class="line">    &#125;</span><br><span class="line">	</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">flush</span><span class="params">(S session)</span> </span>&#123;</span><br><span class="line">        getProcessor(session).flush(session);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">write</span><span class="params">(S session, WriteRequest writeRequest)</span> </span>&#123;</span><br><span class="line">        getProcessor(session).write(session, writeRequest);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">remove</span><span class="params">(S session)</span> </span>&#123;</span><br><span class="line">        getProcessor(session).remove(session);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">updateTrafficControl</span><span class="params">(S session)</span> </span>&#123;</span><br><span class="line">        getProcessor(session).updateTrafficControl(session);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> IoProcessor&lt;S&gt; <span class="title">getProcessor</span><span class="params">(S session)</span> </span>&#123;</span><br><span class="line">        IoProcessor&lt;S&gt; processor = (IoProcessor&lt;S&gt;) session.getAttribute(PROCESSOR);</span><br><span class="line">        <span class="keyword">if</span> (processor == <span class="keyword">null</span>) &#123;</span><br><span class="line">            processor = pool[Math.abs((<span class="keyword">int</span>) session.getId()) % pool.length];</span><br><span class="line">            session.setAttributeIfAbsent(PROCESSOR, processor);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> processor;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>　　SimpleIoProcessorPool初始化过程请详见代码中的注释，这里不在介绍。我们注意到，在其add方法内部，会调用getProcessor方法，从池中选取一个IoProcessor（实际为NioProcessor），并将新生成的session放到该NioProcessor中，实际执行的是其父类AbstractPollingIoProcessor的add方法。NioProcessor类的层次结构为： NioProcessor =&gt; AbstractPollingIoProcessor =&gt; IoProcessor，这里我们直接对这几个类做简要的介绍：<br>　　1. NioProcessor中维护了一个java.nio.channels.Selector，初始化时打开该Selector<br>　　2. AbstractPollingIoProcessor维护了一个内部类Processor，与IoAcceptor中的类似，也是一个Runnable<br>　　3. 每次调用AbstractPollingIoProcessor.add方法添加新的session时，先将session放入一个ConcurrentLinkedQueue中，再新建一个Processor（如果之前没有新建过）放到Executor中执行<br>　　4. mina会在Processor的run方法中，将session的SocketChannel注册到NioProcessor的Selector中，监听OP_READ事件<br>　　5. 同时也会在Processor的run方法中，进行I/O的读写操作<br>　　可以看到，Processor线程主要<strong>负责IO读写操作和执行后面的IoFilterChain和IoHandler逻辑</strong>。Processor线程的数量N默认是CPU数量+1，可以通过配置参数来控制其数量，前面进来的IoSession会被分配到这N个Processor线程中。<br>　　每个Porcessor线程中都维护着一个selector，对它维护的IoSession集合进行select，然后对select的结果进行遍历，逐一处理。像前面提到的，读取数据，以事件的形式通知后面IoFilterChain；以及对写请求队列的flush操作，都是在这类线程中来做的。<br>　　通过将session均分到多个Processor线程里进行处理，可以充分利用多核的处理能力，减轻select操作的压力。但是这样有一个问题：<strong>从单一的Processor线程来看，IO请求的处理流程是单线程顺序处理的。当Process线程select了一批就绪的IO请求后，会在线程内部逐一对这些IO请求进行处理。处理的流程包括IoFilter和IoHandler里的逻辑。当前面的IO请求处理完毕后，才会取下一个IO请求进行处理。如果IoFilter或IoHandler中有比较耗时的操作，Processor线程将会被阻塞住，后续的请求将得不到处理</strong>。这样的情况在高并发的服务器下显然是不能容忍的。于是，Mina通过IoFilter的形式来为处理流程添加线程池。Mina的线程模型主要有以下几种形式：![processor-thread](/images/processor-thread.png<br>　　第一种模型是单线程模型，也是Mina默认线程模型。也就是Processor包办了从底层IO到上层的IoHandler逻辑的所有执行工作。这种模型比较适合于处理逻辑简单，能快速返回的情况。<br>　　第二种模型则是在IoFilterChain中加入了Thread Pool Filter。此时的处理流程变为Processor线程读取完数据后，执行IoFilterChain的逻辑。当执行到Thread Pool Filter的时候，该Filter会将后续的处理流程封装到一个Runnable对象中，并交由Filter自身的线程池来执行，而Processor线程则能立即返回来处理下一个IO请求。这样如果后面的IoFilter或IoHandler中有阻塞操作，只会引起Filter线程池里的线程阻塞，而不会阻塞住Processor线程，从而提高了服务器的处理能力。Mina提供了Thread Pool Filter的一个实现：ExecutorFilter。<br>　　当然，也没有限制说chain中只能添加一个ExecutorFilter，开发者也可以在chain中加入多个ExecutorFilter来构成第三种情况，但一般情况下可能没有这个必要。</p>
<h2 id="IoConnector"><a href="#IoConnector" class="headerlink" title="IoConnector"></a>IoConnector</h2><p>　　上面提到，在服务器端bind一个端口后，会创建一个Acceptor线程来负责监听工作。这个线程的工作只有一个，调用Java NIO接口在该端口上select connect事件，获取新建的连接后，封装成IoSession，交由后面的Processor线程处理。<br>　　在客户端，也有一个叫Connector的线程与之相对应。这两类线程的数量只有1个，外界无法控制这两类线程的数量。具体的源码分析以后再完善！</p>
<h2 id="u603B_u7ED3"><a href="#u603B_u7ED3" class="headerlink" title="总结"></a>总结</h2><p>　　1. IoAcceptor中的selector只注册了ServerSocketChannel，负责处理客户端的连接事件<br>　　2. IoProcessor中的selector注册了N个SocketChannel，负责处理tcp连接的数据读写，一个IoProcessor负责维护多个客户端的连接。IoProcessor的个数默认为cup核数+1<br>　　3. 通过以上方式，实现了客户端连接和io读写的事件分离，将连接和业务处理分开，以得到较高的处理性能！<br>　　通过下图基本可以概括mina的线程调度原理：<img src="/images/mina-thread.png" alt="线程调度原理"></p>
<h2 id="u53C2_u8003_u94FE_u63A5"><a href="#u53C2_u8003_u94FE_u63A5" class="headerlink" title="参考链接"></a>参考链接</h2><p><a href="http://blog.csdn.net/yoara/article/details/37382137" target="_blank" rel="external">http://blog.csdn.net/yoara/article/details/37382137</a><br><a href="http://www.iteye.com/topic/1125178" target="_blank" rel="external">http://www.iteye.com/topic/1125178</a><br><a href="http://www.tuicool.com/articles/eUzUja" target="_blank" rel="external">http://www.tuicool.com/articles/eUzUja</a><br><a href="http://www.cnblogs.com/phinecos/archive/2008/12/03/1347052.html" target="_blank" rel="external">http://www.cnblogs.com/phinecos/archive/2008/12/03/1347052.html</a><br><a href="http://www.tuicool.com/articles/Ujeum2" target="_blank" rel="external">http://www.tuicool.com/articles/Ujeum2</a></p>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://yoursite.com/2016/01/19/nio/18.Mina系列四之IoService/" data-id="cijzzpv79005i1cfsl75fipp5" class="article-share-link">Share</a>
      
      
  <ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/mina/">mina</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/nio/">nio</a></li></ul>

    </footer>
  </div>
  
</article>


  
    <article id="post-nio/17.Mina系列三之IoFilter" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-meta">
    <a href="/2016/01/18/nio/17.Mina系列三之IoFilter/" class="article-date">
  <time datetime="2016-01-18T06:23:00.000Z" itemprop="datePublished">2016-01-18</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/categories/nio/">nio</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2016/01/18/nio/17.Mina系列三之IoFilter/">Mina系列三之IoFilter</a>
    </h1>
  

      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <h2 id="u7B80_u4ECB"><a href="#u7B80_u4ECB" class="headerlink" title="简介"></a>简介</h2><p>　　IoFilter 是 MINA 的核心结构之一，它过滤 IoService 和 IoHandler 之间的所有 I/O 事件和请求，最后将数据交由IoHandler进行处理。这里可以看到Mina的一个设计理念就是<strong>业务代码和数据包处理代码分离</strong>，业务代码只专注于业务逻辑，其他的逻辑如：数据包的解析，封装，过滤等则交由IoFilterChain来处理。IoFilterChain可以看成是Mina处理流程的扩展点。这样的划分使得结构更加清晰，代码分工更明确。开发者通过往Chain中添加IoFilter，来增强处理流程，而不会影响后面的业务逻辑代码。<br>　　这里先直接列出分析的结果，想看源码的朋友可以继续浏览文章！<br>　　1. 过滤器链涉及到的类包括：IoFilterChainBuilder、IoFilterChain、IoFilter、NextFilter和Entry等<br>　　2. Mina在初始化时，通过一个List维护添加进去的链表<br>　　3. 每次有新的客户端连接时，调用IoFilterChainBuilder的buildFilterChain方法，为每个新连接的IoSession构建一个过滤器链IoFilterChain（IoSession在new的同时new一个IoFilterChain）<br>　　4. IoFilterChain内部维护了一个head和tail的Filter，业务代码添加的Filter都放在这两个之间，通过NextFilter相应的方法回调IoFilterChain相应的方法（比如callNextMessageReceived），这样就形成了过滤器链的调用过程<br>　　5. IoFilterChain的内部类TailFilter(过滤器链)最后执行，负责调用业务层的IoHander<br>　　6. 每个过滤器连的上下过滤器、名称等信息，都封装成一个Entry，形成一个过滤器链上的一个节点<br>　　7. Filter执行顺序为：服务端处理连接、数据读取为：head =&gt; tail =&gt; handelr，服务端写入数据为：tail =&gt; head。所以HeadFilter实现了filterWrite方法，TailFilter实现了messageReceived、sessionCreate等方法。该顺序请参考下图：<img src="/images/mina-filter-order.png" alt="IoFilter执行顺序图"><br>　　mina已经内置实现了很多过滤器，如黑名单、压缩和编解码等，如下表所示！</p>
<table>
<thead>
<tr>
<th style="text-align:left">过滤器</th>
<th style="text-align:left">类</th>
<th style="text-align:left">描述</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left">黑名单</td>
<td style="text-align:left">BlacklistFilter</td>
<td style="text-align:left">阻止列入黑名单的远程地址的连接</td>
</tr>
<tr>
<td style="text-align:left">缓冲写</td>
<td style="text-align:left">BufferedWriteFilter</td>
<td style="text-align:left">发送缓存过滤器，缓存发送的消息，避免短小消息频繁发送</td>
</tr>
<tr>
<td style="text-align:left">协议编解码器</td>
<td style="text-align:left">ProtocolCodecFilter</td>
<td style="text-align:left">编解码过滤器，最常用之二</td>
</tr>
<tr>
<td style="text-align:left">压缩</td>
<td style="text-align:left">CompressionFilter</td>
<td style="text-align:left">数据压缩过滤器</td>
</tr>
<tr>
<td style="text-align:left">连接控制</td>
<td style="text-align:left">ConnectionThrottleFilter</td>
<td style="text-align:left">连接控制过滤器，对同一IP地址频繁的创建连接的时间间隔进行控制</td>
</tr>
<tr>
<td style="text-align:left">错误生成</td>
<td style="text-align:left">ErrorGeneratingFilter</td>
<td style="text-align:left"></td>
</tr>
<tr>
<td style="text-align:left">执行者</td>
<td style="text-align:left">ExecutorFilter</td>
<td style="text-align:left">处理线程池过滤器，让每个请求或者事件都通过线程池去执行</td>
</tr>
<tr>
<td style="text-align:left">FileRegion 转换</td>
<td style="text-align:left">FileRegionWriteFilter</td>
<td style="text-align:left">文件转换过滤器，通常应该由IoProcess做这件事，<br>但若需要压缩或者修改时，可通过过滤器链之间的配合来实现</td>
</tr>
<tr>
<td style="text-align:left">保持动态</td>
<td style="text-align:left">KeepAliveFilter</td>
<td style="text-align:left">心跳包过滤器，在idle状态时发送心跳包，并能对超时进行处理</td>
</tr>
<tr>
<td style="text-align:left">日志</td>
<td style="text-align:left">LoggingFilter</td>
<td style="text-align:left">日志记录过滤器，最常用之一</td>
</tr>
<tr>
<td style="text-align:left">MDC 注入</td>
<td style="text-align:left">MdcInjectionFilter</td>
<td style="text-align:left">日志信息注入过滤器，MDC(Mapped Diagnostic Context有译作线程映射表)是日志框架维护的一组信息键值对，<br>可向日志输出信息中插入一些想要显示的内容。</td>
</tr>
<tr>
<td style="text-align:left">等待</td>
<td style="text-align:left">NoopFilter</td>
<td style="text-align:left">用作内部测试的filter，什么也没做</td>
</tr>
<tr>
<td style="text-align:left">分析器</td>
<td style="text-align:left">ProfilerTimerFilter</td>
<td style="text-align:left">时间分析过滤器，记录各种事件消耗的时间</td>
</tr>
<tr>
<td style="text-align:left">代理</td>
<td style="text-align:left">ProxyFilter</td>
<td style="text-align:left">是IoConnector在连接握手时自动加入的过滤器，握手成功后就透明了</td>
</tr>
<tr>
<td style="text-align:left">引用计数</td>
<td style="text-align:left">ReferenceCountingFilter</td>
<td style="text-align:left">引用数过滤器，能记录该过滤器被加入或移除过滤器链的次数，真实使用</td>
</tr>
</tbody>
</table>
<p>时继承它。|<br>|请求响应|RequestResponseFilter|继承WriteRequest|<br>|会话属性初始化|SessionAttributeInitializingFilter|初始化过滤器|<br>|写流|StreamWriteFilter|InputStream直接转换成IoBuffer的过滤器|<br>|SSL过滤器|SslFilter|TCP/IP层面的SSl加解密过滤器|<br>|写请求|WriteRequestFilter|简化IoFilter IoEventType.WRITE事件的实现的抽象过滤器|</p>
<h2 id="IoFilterChainBuilder"><a href="#IoFilterChainBuilder" class="headerlink" title="IoFilterChainBuilder"></a>IoFilterChainBuilder</h2><p>　　在上一篇的示例代码中，我们调用了AbstractIoService的getFilterChain的方法，该方法返回了DefaultIoFilterChainBuilder的一个实例。顾名思义，它是一个IoFilterChain的构造者。下面我们看看它的声明和默认的实现类DefaultIoFilterChainBuilder，这里只贴出了重要的代码。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">IoFilterChainBuilder</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">buildFilterChain</span><span class="params">(IoFilterChain chain)</span> <span class="keyword">throws</span> Exception</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DefaultIoFilterChainBuilder</span> <span class="keyword">implements</span> <span class="title">IoFilterChainBuilder</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> List&lt;Entry&gt; entries;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">DefaultIoFilterChainBuilder</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        entries = <span class="keyword">new</span> CopyOnWriteArrayList&lt;Entry&gt;();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title">addLast</span><span class="params">(String name, IoFilter filter)</span> </span>&#123;</span><br><span class="line">        register(entries.size(), <span class="keyword">new</span> EntryImpl(name, filter));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">buildFilterChain</span><span class="params">(IoFilterChain chain)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        <span class="keyword">for</span> (Entry e : entries) &#123;</span><br><span class="line">            chain.addLast(e.getName(), e.getFilter());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">register</span><span class="params">(<span class="keyword">int</span> index, Entry e)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (contains(e.getName())) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">"Other filter is using the same name: "</span> + e.getName());</span><br><span class="line">        &#125;</span><br><span class="line">        entries.add(index, e);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="class"><span class="keyword">class</span> <span class="title">EntryImpl</span> <span class="keyword">implements</span> <span class="title">Entry</span> </span>&#123;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">final</span> String name;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">volatile</span> IoFilter filter;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">private</span> <span class="title">EntryImpl</span><span class="params">(String name, IoFilter filter)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">this</span>.name = name;</span><br><span class="line">            <span class="keyword">this</span>.filter = filter;</span><br><span class="line">        &#125;</span><br><span class="line">		</span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">addAfter</span><span class="params">(String name, IoFilter filter)</span> </span>&#123;</span><br><span class="line">            DefaultIoFilterChainBuilder.<span class="keyword">this</span>.addAfter(getName(), name, filter);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">addBefore</span><span class="params">(String name, IoFilter filter)</span> </span>&#123;</span><br><span class="line">            DefaultIoFilterChainBuilder.<span class="keyword">this</span>.addBefore(getName(), name, filter);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">remove</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            DefaultIoFilterChainBuilder.<span class="keyword">this</span>.remove(getName());</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">replace</span><span class="params">(IoFilter newFilter)</span> </span>&#123;</span><br><span class="line">            DefaultIoFilterChainBuilder.<span class="keyword">this</span>.replace(getName(), newFilter);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>　　可以看到DefaultIoFilterChainBuilder最重要的方法就是 buildFilterChain ，最重要的一个成员变量就是entries。在Mina初始化时，我们添加了多个IoFilter的实例到entries中。<strong>每次有新的客户端连接时，都会创建一个新的IoSession实例（在其构造函数中初始化一个DefaultIoFilterChain），并调用 buildFilterChain 方法，为新的IoSession的过滤器链初始化</strong>！buildFilterChain 方法内部循环调用了DefaultIoFilterChain的addLast方法，实现了过滤器链的前后链接过程！</p>
<h2 id="DefaultIoFilterChain"><a href="#DefaultIoFilterChain" class="headerlink" title="DefaultIoFilterChain"></a>DefaultIoFilterChain</h2><p>　　我们看看它提供了哪些方法。注意，其内部声明了一个非常重要的Entry接口类，用于记录当前Filter和其下一个Filter<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">IoFilterChain</span> </span>&#123;</span><br><span class="line">    <span class="function">IoSession <span class="title">getSession</span><span class="params">()</span></span>;</span><br><span class="line">    <span class="function">Entry <span class="title">getEntry</span><span class="params">(String name)</span></span>;</span><br><span class="line">    <span class="comment">//省略其他重载方法</span></span><br><span class="line">    <span class="function">IoFilter <span class="title">get</span><span class="params">(String name)</span></span>;</span><br><span class="line">    <span class="comment">//省略其他重载方法</span></span><br><span class="line">    <span class="function">NextFilter <span class="title">getNextFilter</span><span class="params">(Class&lt;? extends IoFilter&gt; filterType)</span></span>;</span><br><span class="line">    <span class="function">List&lt;Entry&gt; <span class="title">getAll</span><span class="params">()</span></span>;</span><br><span class="line">    <span class="function">List&lt;Entry&gt; <span class="title">getAllReversed</span><span class="params">()</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">boolean</span> <span class="title">contains</span><span class="params">(String name)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">addLast</span><span class="params">(String name, IoFilter filter)</span></span>;</span><br><span class="line">    <span class="function">IoFilter <span class="title">replace</span><span class="params">(String name, IoFilter newFilter)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">replace</span><span class="params">(IoFilter oldFilter, IoFilter newFilter)</span></span>;</span><br><span class="line">    <span class="function">IoFilter <span class="title">remove</span><span class="params">(String name)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">remove</span><span class="params">(IoFilter filter)</span></span>;</span><br><span class="line">    <span class="function">IoFilter <span class="title">remove</span><span class="params">(Class&lt;? extends IoFilter&gt; filterType)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">clear</span><span class="params">()</span> <span class="keyword">throws</span> Exception</span>;</span><br><span class="line">    <span class="comment">//省略fireSessionCreated、fireSessionIdle等方法</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">fireMessageReceived</span><span class="params">(Object message)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">fireInputClosed</span><span class="params">()</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">fireFilterWrite</span><span class="params">(WriteRequest writeRequest)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">fireFilterClose</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//维护一个name-filter对的过滤器</span></span><br><span class="line">    <span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Entry</span> </span>&#123;</span><br><span class="line">        <span class="function">String <span class="title">getName</span><span class="params">()</span></span>;</span><br><span class="line">        <span class="function">IoFilter <span class="title">getFilter</span><span class="params">()</span></span>;</span><br><span class="line">        <span class="function">NextFilter <span class="title">getNextFilter</span><span class="params">()</span></span>;</span><br><span class="line">        <span class="function"><span class="keyword">void</span> <span class="title">addBefore</span><span class="params">(String name, IoFilter filter)</span></span>;</span><br><span class="line">        <span class="function"><span class="keyword">void</span> <span class="title">addAfter</span><span class="params">(String name, IoFilter filter)</span></span>;</span><br><span class="line">        <span class="function"><span class="keyword">void</span> <span class="title">replace</span><span class="params">(IoFilter newFilter)</span></span>;</span><br><span class="line">        <span class="function"><span class="keyword">void</span> <span class="title">remove</span><span class="params">()</span></span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DefaultIoFilterChain</span> <span class="keyword">implements</span> <span class="title">IoFilterChain</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> AbstractIoSession session;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Map&lt;String, Entry&gt; name2entry = <span class="keyword">new</span> ConcurrentHashMap&lt;String, Entry&gt;();</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> EntryImpl head;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> EntryImpl tail;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">DefaultIoFilterChain</span><span class="params">(AbstractIoSession session)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.session = session;</span><br><span class="line">        head = <span class="keyword">new</span> EntryImpl(<span class="keyword">null</span>, <span class="keyword">null</span>, <span class="string">"head"</span>, <span class="keyword">new</span> HeadFilter());</span><br><span class="line">        tail = <span class="keyword">new</span> EntryImpl(head, <span class="keyword">null</span>, <span class="string">"tail"</span>, <span class="keyword">new</span> TailFilter());</span><br><span class="line">        head.nextEntry = tail;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//addFirst、addBefore和addAfter等方法</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title">addLast</span><span class="params">(String name, IoFilter filter)</span> </span>&#123;</span><br><span class="line">        checkAddable(name);</span><br><span class="line">        register(tail.prevEntry, name, filter);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> IoFilter <span class="title">remove</span><span class="params">(String name)</span> </span>&#123;</span><br><span class="line">        EntryImpl entry = checkOldName(name);</span><br><span class="line">        deregister(entry);</span><br><span class="line">        <span class="keyword">return</span> entry.getFilter();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title">clear</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        List&lt;IoFilterChain.Entry&gt; l = <span class="keyword">new</span> ArrayList&lt;IoFilterChain.Entry&gt;(name2entry.values());</span><br><span class="line">        <span class="keyword">for</span> (IoFilterChain.Entry entry : l) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                deregister((EntryImpl) entry);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> IoFilterLifeCycleException(<span class="string">"clear(): "</span> + entry.getName() + <span class="string">" in "</span> + getSession(), e);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">register</span><span class="params">(EntryImpl prevEntry, String name, IoFilter filter)</span> </span>&#123;</span><br><span class="line">        EntryImpl newEntry = <span class="keyword">new</span> EntryImpl(prevEntry, prevEntry.nextEntry, name, filter);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            filter.onPreAdd(<span class="keyword">this</span>, name, newEntry.getNextFilter());</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> IoFilterLifeCycleException(<span class="string">"onPreAdd(): "</span> + name + <span class="string">':'</span> + filter + <span class="string">" in "</span> + getSession(), e);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        prevEntry.nextEntry.prevEntry = newEntry;</span><br><span class="line">        prevEntry.nextEntry = newEntry;</span><br><span class="line">        name2entry.put(name, newEntry);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            filter.onPostAdd(<span class="keyword">this</span>, name, newEntry.getNextFilter());</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            deregister0(newEntry);</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> IoFilterLifeCycleException(<span class="string">"onPostAdd(): "</span> + name + <span class="string">':'</span> + filter + <span class="string">" in "</span> + getSession(), e);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">deregister</span><span class="params">(EntryImpl entry)</span> </span>&#123;</span><br><span class="line">        IoFilter filter = entry.getFilter();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            filter.onPreRemove(<span class="keyword">this</span>, entry.getName(), entry.getNextFilter());</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> IoFilterLifeCycleException(<span class="string">"onPreRemove(): "</span> + entry.getName() + <span class="string">':'</span> + filter + <span class="string">" in "</span></span><br><span class="line">                    + getSession(), e);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        deregister0(entry);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            filter.onPostRemove(<span class="keyword">this</span>, entry.getName(), entry.getNextFilter());</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> IoFilterLifeCycleException(<span class="string">"onPostRemove(): "</span> + entry.getName() + <span class="string">':'</span> + filter + <span class="string">" in "</span></span><br><span class="line">                    + getSession(), e);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">deregister0</span><span class="params">(EntryImpl entry)</span> </span>&#123;</span><br><span class="line">        EntryImpl prevEntry = entry.prevEntry;</span><br><span class="line">        EntryImpl nextEntry = entry.nextEntry;</span><br><span class="line">        prevEntry.nextEntry = nextEntry;</span><br><span class="line">        nextEntry.prevEntry = prevEntry;</span><br><span class="line"></span><br><span class="line">        name2entry.remove(entry.name);</span><br><span class="line">    &#125;</span><br><span class="line">	</span><br><span class="line">	<span class="comment">//省略其他的一些fireSessionCreated、callNextSessionCreated等方法</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">fireMessageReceived</span><span class="params">(Object message)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (message <span class="keyword">instanceof</span> IoBuffer) &#123;</span><br><span class="line">            session.increaseReadBytes(((IoBuffer) message).remaining(), System.currentTimeMillis());</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        callNextMessageReceived(head, session, message);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//调用下一个filter</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">callNextMessageReceived</span><span class="params">(Entry entry, IoSession session, Object message)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            IoFilter filter = entry.getFilter();</span><br><span class="line">            NextFilter nextFilter = entry.getNextFilter();</span><br><span class="line">            filter.messageReceived(nextFilter, session, message);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            fireExceptionCaught(e);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Error e) &#123;</span><br><span class="line">            fireExceptionCaught(e);</span><br><span class="line">            <span class="keyword">throw</span> e;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="class"><span class="keyword">class</span> <span class="title">HeadFilter</span> <span class="keyword">extends</span> <span class="title">IoFilterAdapter</span> </span>&#123;</span><br><span class="line">        <span class="annotation">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">filterWrite</span><span class="params">(NextFilter nextFilter, IoSession session, WriteRequest writeRequest)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line"></span><br><span class="line">            AbstractIoSession s = (AbstractIoSession) session;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// Maintain counters.</span></span><br><span class="line">            <span class="keyword">if</span> (writeRequest.getMessage() <span class="keyword">instanceof</span> IoBuffer) &#123;</span><br><span class="line">                IoBuffer buffer = (IoBuffer) writeRequest.getMessage();</span><br><span class="line">                <span class="comment">// I/O processor implementation will call buffer.reset()</span></span><br><span class="line">                <span class="comment">// it after the write operation is finished, because</span></span><br><span class="line">                <span class="comment">// the buffer will be specified with messageSent event.</span></span><br><span class="line">                buffer.mark();</span><br><span class="line">                <span class="keyword">int</span> remaining = buffer.remaining();</span><br><span class="line"></span><br><span class="line">                <span class="keyword">if</span> (remaining &gt; <span class="number">0</span>) &#123;</span><br><span class="line">                    s.increaseScheduledWriteBytes(remaining);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                s.increaseScheduledWriteMessages();</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            WriteRequestQueue writeRequestQueue = s.getWriteRequestQueue();</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (!s.isWriteSuspended()) &#123;</span><br><span class="line">                <span class="keyword">if</span> (writeRequestQueue.isEmpty(session)) &#123;</span><br><span class="line">                    <span class="comment">// We can write directly the message</span></span><br><span class="line">                    s.getProcessor().write(s, writeRequest);</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    s.getWriteRequestQueue().offer(s, writeRequest);</span><br><span class="line">                    s.getProcessor().flush(s);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                s.getWriteRequestQueue().offer(s, writeRequest);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="annotation">@SuppressWarnings</span>(<span class="string">"unchecked"</span>)</span><br><span class="line">        <span class="annotation">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">filterClose</span><span class="params">(NextFilter nextFilter, IoSession session)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">            ((AbstractIoSession) session).getProcessor().remove(session);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">TailFilter</span> <span class="keyword">extends</span> <span class="title">IoFilterAdapter</span> </span>&#123;</span><br><span class="line">        <span class="comment">//省略sessionCreated、messageSent等方法</span></span><br><span class="line">        <span class="annotation">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">messageReceived</span><span class="params">(NextFilter nextFilter, IoSession session, Object message)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">            AbstractIoSession s = (AbstractIoSession) session;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (!(message <span class="keyword">instanceof</span> IoBuffer)) &#123;</span><br><span class="line">                s.increaseReadMessages(System.currentTimeMillis());</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (!((IoBuffer) message).hasRemaining()) &#123;</span><br><span class="line">                s.increaseReadMessages(System.currentTimeMillis());</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// Update the statistics</span></span><br><span class="line">            <span class="keyword">if</span> (session.getService() <span class="keyword">instanceof</span> AbstractIoService) &#123;</span><br><span class="line">                ((AbstractIoService) session.getService()).getStatistics().updateThroughput(System.currentTimeMillis());</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// Propagate the message</span></span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                session.getHandler().messageReceived(s, message);</span><br><span class="line">            &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                <span class="keyword">if</span> (s.getConfig().isUseReadOperation()) &#123;</span><br><span class="line">                    s.offerReadFuture(message);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="class"><span class="keyword">class</span> <span class="title">EntryImpl</span> <span class="keyword">implements</span> <span class="title">Entry</span> </span>&#123;</span><br><span class="line">        <span class="keyword">private</span> EntryImpl prevEntry;</span><br><span class="line">        <span class="keyword">private</span> EntryImpl nextEntry;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">final</span> String name;</span><br><span class="line">        <span class="keyword">private</span> IoFilter filter;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">final</span> NextFilter nextFilter;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">private</span> <span class="title">EntryImpl</span><span class="params">(EntryImpl prevEntry, EntryImpl nextEntry, String name, IoFilter filter)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">this</span>.prevEntry = prevEntry;</span><br><span class="line">            <span class="keyword">this</span>.nextEntry = nextEntry;</span><br><span class="line">            <span class="keyword">this</span>.name = name;</span><br><span class="line">            <span class="keyword">this</span>.filter = filter;</span><br><span class="line">            <span class="keyword">this</span>.nextFilter = <span class="keyword">new</span> NextFilter() &#123;</span><br><span class="line">                <span class="comment">////其他的一些方法，比如sessionOpened、sessionIdle等省略</span></span><br><span class="line">                <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">messageReceived</span><span class="params">(IoSession session, Object message)</span> </span>&#123;</span><br><span class="line">                    Entry nextEntry = EntryImpl.<span class="keyword">this</span>.nextEntry;</span><br><span class="line">                    callNextMessageReceived(nextEntry, session, message);</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">filterWrite</span><span class="params">(IoSession session, WriteRequest writeRequest)</span> </span>&#123;</span><br><span class="line">                    Entry nextEntry = EntryImpl.<span class="keyword">this</span>.prevEntry;</span><br><span class="line">                    callPreviousFilterWrite(nextEntry, session, writeRequest);</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">filterClose</span><span class="params">(IoSession session)</span> </span>&#123;</span><br><span class="line">                    Entry nextEntry = EntryImpl.<span class="keyword">this</span>.prevEntry;</span><br><span class="line">                    callPreviousFilterClose(nextEntry, session);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>　　DefaultIoFilterChain内部维护了过滤器链的首尾两个节点：head和tail，均为EntryImpl的实例。每个EntryImpl都存在上一个节点和下一个节点，这样就形成了一个链表关系。对下一个节点的sessionCreated、sessionIdle等方法的调用都是通过匿名类NextFilter提供的方法调用的。<br>　　其内部维护的IoFilterAdapter类型的tail作为过滤器链的最后一个环节，负责调用IoHander相应的方法！下面看看接口IoFilter及其内部接口NextFilter。</p>
<h2 id="IoFilter"><a href="#IoFilter" class="headerlink" title="IoFilter"></a>IoFilter</h2><p>　　IoFilter作为所有过滤器的基础接口，提供了过滤器从初始化、数据读写和销毁的通用方法。需要特别说明的是其声明了一个内部接口NextFilter，用于维护过滤器链的前后级调用过程！虽然有IoFilter和NextFilter两个接口，接口方法都差不多，但最后真正业务的执行者还是IoFilter的实现，IoFilterAdapter做为它的默认实现，完成了适配器的功能，以后的类可以直接继承它而不用实现IoFilter接口，想实现哪个方法只需要覆盖IoFilterAdapter的类的方法即可。NextFilter只起到转发的作用，如消息接收处理方法（messageReceived）：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">IoFilter</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">init</span><span class="params">()</span> <span class="keyword">throws</span> Exception</span>;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">destroy</span><span class="params">()</span> <span class="keyword">throws</span> Exception</span>;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">onPreAdd</span><span class="params">(IoFilterChain parent, String name, NextFilter nextFilter)</span> <span class="keyword">throws</span> Exception</span>;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">onPostAdd</span><span class="params">(IoFilterChain parent, String name, NextFilter nextFilter)</span> <span class="keyword">throws</span> Exception</span>;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">onPreRemove</span><span class="params">(IoFilterChain parent, String name, NextFilter nextFilter)</span> <span class="keyword">throws</span> Exception</span>;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">onPostRemove</span><span class="params">(IoFilterChain parent, String name, NextFilter nextFilter)</span> <span class="keyword">throws</span> Exception</span>;</span><br><span class="line">    <span class="comment">//省略sessionCreated、sessionIdle等方法</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">messageReceived</span><span class="params">(NextFilter nextFilter, IoSession session, Object message)</span> <span class="keyword">throws</span> Exception</span>;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">filterClose</span><span class="params">(NextFilter nextFilter, IoSession session)</span> <span class="keyword">throws</span> Exception</span>;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">filterWrite</span><span class="params">(NextFilter nextFilter, IoSession session, WriteRequest writeRequest)</span> <span class="keyword">throws</span> Exception</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">NextFilter</span> </span>&#123;</span><br><span class="line">        <span class="comment">//省略sessionCreated、sessionIdle等方法</span></span><br><span class="line">        <span class="function"><span class="keyword">void</span> <span class="title">inputClosed</span><span class="params">(IoSession session)</span></span>;</span><br><span class="line">        <span class="function"><span class="keyword">void</span> <span class="title">messageReceived</span><span class="params">(IoSession session, Object message)</span></span>;</span><br><span class="line">        <span class="function"><span class="keyword">void</span> <span class="title">filterWrite</span><span class="params">(IoSession session, WriteRequest writeRequest)</span></span>;</span><br><span class="line">        <span class="function"><span class="keyword">void</span> <span class="title">filterClose</span><span class="params">(IoSession session)</span></span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">IoFilterAdapter</span> <span class="keyword">implements</span> <span class="title">IoFilter</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">init</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>&#123;&#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">destroy</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>&#123;&#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onPreAdd</span><span class="params">(IoFilterChain parent, String name, NextFilter nextFilter)</span> <span class="keyword">throws</span> Exception </span>&#123;&#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onPostAdd</span><span class="params">(IoFilterChain parent, String name, NextFilter nextFilter)</span> <span class="keyword">throws</span> Exception </span>&#123;&#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onPreRemove</span><span class="params">(IoFilterChain parent, String name, NextFilter nextFilter)</span> <span class="keyword">throws</span> Exception </span>&#123;&#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onPostRemove</span><span class="params">(IoFilterChain parent, String name, NextFilter nextFilter)</span> <span class="keyword">throws</span> Exception </span>&#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//sessionOpened等方法类型</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">messageReceived</span><span class="params">(NextFilter nextFilter, IoSession session, Object message)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        nextFilter.messageReceived(session, message);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<h2 id="u81EA_u5B9A_u4E49_u8FC7_u6EE4_u5668"><a href="#u81EA_u5B9A_u4E49_u8FC7_u6EE4_u5668" class="headerlink" title="自定义过滤器"></a>自定义过滤器</h2><p>　　下面我们实现两个过滤器：MyFilter1和MyFilter2，运行后两个过滤器的sessionCreated方法都执行了，因为MyFilter1的sessionCreated方法调用了父类IoFilterAdapter的方法，父类方法又执行了nextFilter.sessionCreated(session, message);。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyFilter1</span> <span class="keyword">extends</span> <span class="title">IoFilterAdapter</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="annotation">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">sessionCreated</span><span class="params">(NextFilter nextFilter, IoSession session)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"MyFilter1 sessionCreated"</span>);</span><br><span class="line">        <span class="comment">//屏蔽以下代码后不会再调用MyFilter2的sessionCreated方法</span></span><br><span class="line">        <span class="keyword">super</span>.sessionCreated(nextFilter, session);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyFilter2</span> <span class="keyword">extends</span> <span class="title">IoFilterAdapter</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="annotation">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">sessionCreated</span><span class="params">(NextFilter nextFilter, IoSession session)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"MyFilter2 sessionCreated"</span>);</span><br><span class="line">        <span class="keyword">super</span>.sessionCreated(nextFilter, session);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<h2 id="BlacklistFilter"><a href="#BlacklistFilter" class="headerlink" title="BlacklistFilter"></a>BlacklistFilter</h2><p>　　考虑到系统安全性的问题，我们经常需要限制某些客户端的访问，此时就用到了mina为我们提供的黑名单过滤器，核心代码如下：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BlacklistFilter</span> <span class="keyword">extends</span> <span class="title">IoFilterAdapter</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> List&lt;Subnet&gt; blacklist = <span class="keyword">new</span> CopyOnWriteArrayList&lt;Subnet&gt;();</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setBlacklist</span><span class="params">(InetAddress[] addresses)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (addresses == <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">"addresses"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        blacklist.clear();</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; addresses.length; i++) &#123;</span><br><span class="line">            InetAddress addr = addresses[i];</span><br><span class="line">            block(addr);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">	</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setSubnetBlacklist</span><span class="params">(Subnet[] subnets)</span> </span>&#123;</span><br><span class="line">        blacklist.clear();</span><br><span class="line">        <span class="keyword">for</span> (Subnet subnet : subnets) &#123;</span><br><span class="line">            block(subnet);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">	</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">block</span><span class="params">(InetAddress address)</span> </span>&#123;</span><br><span class="line">        block(<span class="keyword">new</span> Subnet(address, <span class="number">32</span>));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">block</span><span class="params">(Subnet subnet)</span> </span>&#123;</span><br><span class="line">        blacklist.add(subnet);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">unblock</span><span class="params">(InetAddress address)</span> </span>&#123;</span><br><span class="line">        unblock(<span class="keyword">new</span> Subnet(address, <span class="number">32</span>));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">unblock</span><span class="params">(Subnet subnet)</span> </span>&#123;</span><br><span class="line">        blacklist.remove(subnet);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//sessionOpened、sessionIdle等方法的处理逻辑与sessionCreated一致</span></span><br><span class="line">    <span class="annotation">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">sessionCreated</span><span class="params">(NextFilter nextFilter, IoSession session)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (!isBlocked(session)) &#123;</span><br><span class="line">            nextFilter.sessionCreated(session);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            blockSession(session);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">blockSession</span><span class="params">(IoSession session)</span> </span>&#123;</span><br><span class="line">        LOGGER.warn(<span class="string">"Remote address in the blacklist; closing."</span>);</span><br><span class="line">        session.close(<span class="keyword">true</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">boolean</span> <span class="title">isBlocked</span><span class="params">(IoSession session)</span> </span>&#123;</span><br><span class="line">        SocketAddress remoteAddress = session.getRemoteAddress();</span><br><span class="line">        <span class="keyword">if</span> (remoteAddress <span class="keyword">instanceof</span> InetSocketAddress) &#123;</span><br><span class="line">            InetAddress address = ((InetSocketAddress) remoteAddress).getAddress();</span><br><span class="line">            <span class="keyword">for</span> (Subnet subnet : blacklist) &#123;</span><br><span class="line">                <span class="keyword">if</span> (subnet.inSubnet(address)) &#123;</span><br><span class="line">                    <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>　　可以看到，在BlacklistFilter过滤器中维护了一个黑名单列表，每次sessionCreated或者messageReceived执行时，都需要检测客户端的ip是否在黑名单中，不在则正常处理，在则关闭连接。至于在sessionCreated、messageReceived等方法中都需要判断，是为了支持动态添加黑名单的能力！</p>
<h2 id="ConnectionThrottleFilter"><a href="#ConnectionThrottleFilter" class="headerlink" title="ConnectionThrottleFilter"></a>ConnectionThrottleFilter</h2><p>　　同样考虑到客户端短时间进行大量连接的问题，防止系统撑爆，通过该过滤器限制客户端的连接频率！<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ConnectionThrottleFilter</span> <span class="keyword">extends</span> <span class="title">IoFilterAdapter</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">long</span> DEFAULT_TIME = <span class="number">1000</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">long</span> allowedInterval;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Map&lt;String, Long&gt; clients;</span><br><span class="line">    <span class="keyword">private</span> Lock lock = <span class="keyword">new</span> ReentrantLock();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="class"><span class="keyword">class</span> <span class="title">ExpiredSessionThread</span> <span class="keyword">extends</span> <span class="title">Thread</span> </span>&#123;</span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                Thread.sleep(allowedInterval);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                <span class="keyword">return</span>;</span><br><span class="line">            &#125;</span><br><span class="line">			</span><br><span class="line">            <span class="keyword">long</span> currentTime = System.currentTimeMillis();</span><br><span class="line">            lock.lock();</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                Iterator&lt;String&gt; sessions = clients.keySet().iterator();</span><br><span class="line"></span><br><span class="line">                <span class="keyword">while</span> (sessions.hasNext()) &#123;</span><br><span class="line">                    String session = sessions.next();</span><br><span class="line">                    <span class="keyword">long</span> creationTime = clients.get(session);</span><br><span class="line">                    <span class="keyword">if</span> (creationTime + allowedInterval &lt; currentTime) &#123;</span><br><span class="line">                        clients.remove(session);</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                lock.unlock();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">	</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">ConnectionThrottleFilter</span><span class="params">(<span class="keyword">long</span> allowedInterval)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.allowedInterval = allowedInterval;</span><br><span class="line">        clients = <span class="keyword">new</span> ConcurrentHashMap&lt;String, Long&gt;();</span><br><span class="line"></span><br><span class="line">        ExpiredSessionThread cleanupThread = <span class="keyword">new</span> ExpiredSessionThread();</span><br><span class="line">        cleanupThread.setDaemon(<span class="keyword">true</span>);</span><br><span class="line">        cleanupThread.start();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setAllowedInterval</span><span class="params">(<span class="keyword">long</span> allowedInterval)</span> </span>&#123;</span><br><span class="line">        lock.lock();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">this</span>.allowedInterval = allowedInterval;</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            lock.unlock();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">	</span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">boolean</span> <span class="title">isConnectionOk</span><span class="params">(IoSession session)</span> </span>&#123;</span><br><span class="line">        SocketAddress remoteAddress = session.getRemoteAddress();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (remoteAddress <span class="keyword">instanceof</span> InetSocketAddress) &#123;</span><br><span class="line">            InetSocketAddress addr = (InetSocketAddress) remoteAddress;</span><br><span class="line">            <span class="keyword">long</span> now = System.currentTimeMillis();</span><br><span class="line"></span><br><span class="line">            lock.lock();</span><br><span class="line"></span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="keyword">if</span> (clients.containsKey(addr.getAddress().getHostAddress())) &#123;</span><br><span class="line"></span><br><span class="line">                    LOGGER.debug(<span class="string">"This is not a new client"</span>);</span><br><span class="line">                    Long lastConnTime = clients.get(addr.getAddress().getHostAddress());</span><br><span class="line"></span><br><span class="line">                    clients.put(addr.getAddress().getHostAddress(), now);</span><br><span class="line"></span><br><span class="line">                    <span class="comment">// if the interval between now and the last connection is</span></span><br><span class="line">                    <span class="comment">// less than the allowed interval, return false</span></span><br><span class="line">                    <span class="keyword">if</span> (now - lastConnTime &lt; allowedInterval) &#123;</span><br><span class="line">                        LOGGER.warn(<span class="string">"Session connection interval too short"</span>);</span><br><span class="line">                        <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">                    &#125;</span><br><span class="line"></span><br><span class="line">                    <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                clients.put(addr.getAddress().getHostAddress(), now);</span><br><span class="line">            &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                lock.unlock();</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="annotation">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">sessionCreated</span><span class="params">(NextFilter nextFilter, IoSession session)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (!isConnectionOk(session)) &#123;</span><br><span class="line">            LOGGER.warn(<span class="string">"Connections coming in too fast; closing."</span>);</span><br><span class="line">            session.close(<span class="keyword">true</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        nextFilter.sessionCreated(session);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>　　每次新客户端连接时，获取其ip和当前时间。先判断Map中（ip=&gt;time）是否存在该客户端的ip，存在则判断上次连接时间与本次连接时间间隔，比预设的小则管理，否则正常流程。不存在则直接方法Map中。</p>
<h2 id="u6570_u636E_u8F6C_u6362"><a href="#u6570_u636E_u8F6C_u6362" class="headerlink" title="数据转换"></a>数据转换</h2><p>　　如果需要自定义过滤器，实现对发送的数据类型进行转换，最好同时实现 filterWrite() 和 messageSent() 方法，如下所示。我们可以参考 WriteRequestFilter 和 AbstractStreamWriteFilter 等的实现。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">filterWrite</span><span class="params">(NextFilter nextFilter, IoSession session, WriteRequest request)</span> </span>&#123;  </span><br><span class="line">    nextFilter.filterWrite(  </span><br><span class="line">        session, <span class="keyword">new</span> DefaultWriteRequest(((String) request.getMessage()).toCharArray(), request.getFuture(), request.getDestination()));  </span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">messageSent</span><span class="params">(NextFilter nextFilter, IoSession session, Object message)</span> </span>&#123;  </span><br><span class="line">    nextFilter.messageSent(session, <span class="keyword">new</span> String((<span class="keyword">char</span>[]) message));  </span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//下面的实现方法更加高效</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">filterWrite</span><span class="params">(NextFilter nextFilter, IoSession session, WriteRequest request)</span> </span>&#123;  </span><br><span class="line">    String m = (String) request.getMessage();  </span><br><span class="line">    ByteBuffer newBuffer = <span class="keyword">new</span> MyByteBuffer(m, ByteBuffer.wrap(m.getBytes());  </span><br><span class="line">  </span><br><span class="line">    nextFilter.filterWrite(  </span><br><span class="line">            session, <span class="keyword">new</span> WriteRequest(newBuffer, request.getFuture(), request.getDestination()));  </span><br><span class="line">&#125;  </span><br><span class="line">  </span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">messageSent</span><span class="params">(NextFilter nextFilter, IoSession session, Object message)</span> </span>&#123;  </span><br><span class="line">    <span class="keyword">if</span> (message <span class="keyword">instanceof</span> MyByteBuffer) &#123;  </span><br><span class="line">        nextFilter.messageSent(session, ((MyByteBuffer) message).originalValue);  </span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;  </span><br><span class="line">        nextFilter.messageSent(session, message);  </span><br><span class="line">    &#125;  </span><br><span class="line">&#125;  </span><br><span class="line">  </span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">MyByteBuffer</span> <span class="keyword">extends</span> <span class="title">ByteBufferProxy</span> </span>&#123;  </span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Object originalValue;  </span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="title">MyByteBuffer</span><span class="params">(Object originalValue, ByteBuffer encodedValue)</span> </span>&#123;  </span><br><span class="line">        <span class="keyword">super</span>(encodedValue);  </span><br><span class="line">        <span class="keyword">this</span>.originalValue = originalValue;  </span><br><span class="line">    &#125;  </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<h2 id="ExecutorFilter"><a href="#ExecutorFilter" class="headerlink" title="ExecutorFilter"></a>ExecutorFilter</h2><p>　　首先，一个客户端连接只使用一个线程NioProcessor，如果handler的业务比较耗时，会导致NioProcessor线程堵塞。一个NioProcessor线程会处理多个客户端的连接，这样某个handler耗时处理就会影响其他客户端的处理。此时需要采用独立的线程处理Handler，在 FilterChain 的最后增加一个ExecutorFitler即可！<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">//客户端在sessionOpened方法中发送五条消息</span></span><br><span class="line"><span class="annotation">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">sessionOpened</span><span class="params">(IoSession session)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">    session.write(<span class="string">"hello1"</span>);</span><br><span class="line">    session.write(<span class="string">"hello2"</span>);</span><br><span class="line">    session.write(<span class="string">"hello3"</span>);</span><br><span class="line">    session.write(<span class="string">"hello4"</span>);</span><br><span class="line">    session.write(<span class="string">"hello5"</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//服务端增加过滤器</span></span><br><span class="line">acceptor.getFilterChain().addLast(<span class="string">"threadPool"</span>, <span class="keyword">new</span> ExecutorFilter(Executors.newCachedThreadPool()));</span><br><span class="line"></span><br><span class="line"><span class="comment">//服务端的messageReceived方法修改后如下</span></span><br><span class="line"><span class="annotation">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">messageReceived</span><span class="params">( IoSession session, Object message )</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line"></span><br><span class="line">	Thread.sleep(<span class="number">2000</span>);</span><br><span class="line">	<span class="comment">//不添加ExecutorFilter时输出：Handler处理时线程：NioProcessor-2</span></span><br><span class="line">	<span class="comment">// 每隔2秒输出一次消息</span></span><br><span class="line"></span><br><span class="line">	<span class="comment">//添加ExecutorFilter时输出：Handler处理时线程：pool-3-thread-1、Handler处理时线程：pool-3-thread-3等</span></span><br><span class="line">	<span class="comment">//五条消息几乎同时输出</span></span><br><span class="line">	System.out.println(<span class="string">"Handler处理时线程："</span> + Thread.currentThread().getName());</span><br><span class="line">	</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>　　在处理流程中加入线程池ExecutorFilter过滤器，可以较好的提高服务器的吞吐量，但也带来了新的问题：<strong>同一个IoSession的多个IO请求可能被ExecutorFilter并行的处理</strong>，这对于一些对请求处理顺序有要求的程序来说是不希望看到的。比如：数据库服务器处理同一个会话里的prepare，execute，commit请求希望是能按顺序逐一执行的。<br>　　Mina里默认的实现是有保证同一个IoSession中IO请求的顺序的。具体的实现是，ExecutorFilter默认采用了Mina提供的OrderedThreadPoolExecutor作为内置线程池。后者并不会立即执行加入进来的Runnable对象，而是会先从Runnable对象里获取关联的IoSession(这里有个down cast成IoEvent的操作)，并将Runnable对象加入到session的任务列表中。OrderedThreadPoolExecutor会按session里任务列表的顺序来处理请求，从而保证了请求的执行顺序。<br>　　对于没有顺序要请求的情况，可以为ExecutorFilter指定一个Executor来替换掉默认的OrderedThreadPoolExecutor，让同一个session的多个请求能被并行地处理，来进一步提高吞吐量。</p>
<h2 id="u53C2_u8003_u94FE_u63A5"><a href="#u53C2_u8003_u94FE_u63A5" class="headerlink" title="参考链接"></a>参考链接</h2><p><a href="http://mina.apache.org/mina-project/quick-start-guide.html" target="_blank" rel="external">http://mina.apache.org/mina-project/quick-start-guide.html</a><br><a href="http://blog.csdn.net/yoara/article/details/37597141" target="_blank" rel="external">http://blog.csdn.net/yoara/article/details/37597141</a></p>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://yoursite.com/2016/01/18/nio/17.Mina系列三之IoFilter/" data-id="cijzzpv7c005m1cfsjnkss18g" class="article-share-link">Share</a>
      
      
  <ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/mina/">mina</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/nio/">nio</a></li></ul>

    </footer>
  </div>
  
</article>


  
    <article id="post-nio/16.Mina系列二之示例" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-meta">
    <a href="/2016/01/18/nio/16.Mina系列二之示例/" class="article-date">
  <time datetime="2016-01-18T06:22:00.000Z" itemprop="datePublished">2016-01-18</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/categories/nio/">nio</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2016/01/18/nio/16.Mina系列二之示例/">Mina系列二之示例</a>
    </h1>
  

      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <h2 id="u7B80_u4ECB"><a href="#u7B80_u4ECB" class="headerlink" title="简介"></a>简介</h2><p>　　这里按照Mina官方示例给出的代码，实现了一个打印服务端日期的示例，客户端直接在用telnet进行连接。我们先看服务端和客户端相关类的UML图：<img src="/images/mina-uml-server.jpg" alt="服务端UML"><img src="/images/mina-uml-client.jpg" alt="客户端UML">
　　</p>
<h2 id="u670D_u52A1_u7AEF_u5B9E_u73B0"><a href="#u670D_u52A1_u7AEF_u5B9E_u73B0" class="headerlink" title="服务端实现"></a>服务端实现</h2><p>　　一个基本的nio服务需要包括：监听指定的端口，对收到的消息进行解码，对解码后的消息进行业务处理等功能。同时也可以设置tcp的noDelay参数，空闲连接等。按照这几点，我们看看下面的实现代码！<br>　　本示例的服务端在收到客户端的消息后，会向客户端写入当前日期信息，同时监听读写空闲！<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MinaTimeServer</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> PORT = <span class="number">9123</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">( String[] args )</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">        NioSocketAcceptor acceptor = <span class="keyword">new</span> NioSocketAcceptor();</span><br><span class="line"></span><br><span class="line">        acceptor.getFilterChain().addLast( <span class="string">"logger"</span>, <span class="keyword">new</span> LoggingFilter() );</span><br><span class="line">        acceptor.getFilterChain().addLast( <span class="string">"codec"</span>, <span class="keyword">new</span> ProtocolCodecFilter( <span class="keyword">new</span> TextLineCodecFactory( Charset.forName(<span class="string">"UTF-8"</span>))));</span><br><span class="line"></span><br><span class="line">        acceptor.setHandler(<span class="keyword">new</span> TimeServerHandler());</span><br><span class="line"></span><br><span class="line">        acceptor.getSessionConfig().setReadBufferSize( <span class="number">2048</span> );</span><br><span class="line">        acceptor.getSessionConfig().setIdleTime( IdleStatus.BOTH_IDLE, <span class="number">10</span> );</span><br><span class="line">        <span class="comment">//SocketSessionConfig继承自IoSessionConfig</span></span><br><span class="line">        acceptor.getSessionConfig().setTcpNoDelay(<span class="keyword">true</span>);</span><br><span class="line"></span><br><span class="line">        acceptor.bind(<span class="keyword">new</span> InetSocketAddress(PORT));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">TimeServerHandler</span> <span class="keyword">extends</span> <span class="title">IoHandlerAdapter</span> </span>&#123;</span><br><span class="line">        <span class="annotation">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">exceptionCaught</span><span class="params">( IoSession session, Throwable cause )</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">            cause.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="annotation">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">messageReceived</span><span class="params">( IoSession session, Object message )</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">            String str = message.toString();</span><br><span class="line">            <span class="keyword">if</span>( str.trim().equalsIgnoreCase(<span class="string">"quit"</span>) ) &#123;</span><br><span class="line">                session.close(<span class="keyword">true</span>);</span><br><span class="line">                <span class="keyword">return</span>;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            Date date = <span class="keyword">new</span> Date();</span><br><span class="line">            session.write( date.toString() );</span><br><span class="line">            System.out.println(<span class="string">"Message written..."</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="annotation">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">sessionIdle</span><span class="params">( IoSession session, IdleStatus status )</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">            System.out.println( <span class="string">"IDLE "</span> + session.getIdleCount( status ));</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<h2 id="u5BA2_u6237_u7AEF_u8FDE_u63A5"><a href="#u5BA2_u6237_u7AEF_u8FDE_u63A5" class="headerlink" title="客户端连接"></a>客户端连接</h2><p>　　直接采用telnet客户端进行连接,发送数据即可看到结果！这里我们采用Mina实现一个客户端的的方式。该客户端在与服务端连接后，向服务端发送一条数据，并打印收到的消息！<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MinaTimeClient</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> PORT = <span class="number">9123</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">( String[] args )</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">        NioSocketConnector acceptor = <span class="keyword">new</span> NioSocketConnector();</span><br><span class="line"></span><br><span class="line">        acceptor.getFilterChain().addLast( <span class="string">"logger"</span>, <span class="keyword">new</span> LoggingFilter() );</span><br><span class="line">        acceptor.getFilterChain().addLast( <span class="string">"codec"</span>, <span class="keyword">new</span> ProtocolCodecFilter( <span class="keyword">new</span> TextLineCodecFactory( Charset.forName(<span class="string">"UTF-8"</span>))));</span><br><span class="line"></span><br><span class="line">        acceptor.setHandler(<span class="keyword">new</span> TimeServerHandler());</span><br><span class="line"></span><br><span class="line">        acceptor.getSessionConfig().setReadBufferSize( <span class="number">2048</span> );</span><br><span class="line">        acceptor.getSessionConfig().setIdleTime( IdleStatus.BOTH_IDLE, <span class="number">10</span> );</span><br><span class="line">        <span class="comment">//SocketSessionConfig继承自IoSessionConfig</span></span><br><span class="line">        acceptor.getSessionConfig().setTcpNoDelay(<span class="keyword">true</span>);</span><br><span class="line"></span><br><span class="line">        acceptor.connect(<span class="keyword">new</span> InetSocketAddress(PORT));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">TimeServerHandler</span> <span class="keyword">extends</span> <span class="title">IoHandlerAdapter</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">        <span class="annotation">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">sessionOpened</span><span class="params">(IoSession session)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">            <span class="comment">// Empty handler</span></span><br><span class="line">            session.write(<span class="string">"hello"</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="annotation">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">exceptionCaught</span><span class="params">( IoSession session, Throwable cause )</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">            cause.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="annotation">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">messageReceived</span><span class="params">( IoSession session, Object message )</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">            String str = message.toString();</span><br><span class="line">            System.out.println(str);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="annotation">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">sessionIdle</span><span class="params">( IoSession session, IdleStatus status )</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">            System.out.println( <span class="string">"IDLE "</span> + session.getIdleCount( status ));</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<h2 id="u603B_u7ED3"><a href="#u603B_u7ED3" class="headerlink" title="总结"></a>总结</h2><p>　　从开篇的uml图中可以看到，Mina抽象出了IoAcceptor和IoConnector的概念，它们同属于IoService。同时实现了过滤器链，实现消息的编解码等操作。通过IoHandler实现对各种IO事件，比如：连接、异常、收发消息和空闲等的处理。后续系列文章我们将逐一解答这些功能的实现原理！</p>
<h2 id="u53C2_u8003_u94FE_u63A5"><a href="#u53C2_u8003_u94FE_u63A5" class="headerlink" title="参考链接"></a>参考链接</h2><p><a href="http://mina.apache.org/mina-project/quick-start-guide.html" target="_blank" rel="external">http://mina.apache.org/mina-project/quick-start-guide.html</a></p>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://yoursite.com/2016/01/18/nio/16.Mina系列二之示例/" data-id="cijzzpv7h005q1cfss1z78e99" class="article-share-link">Share</a>
      
      
  <ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/mina/">mina</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/nio/">nio</a></li></ul>

    </footer>
  </div>
  
</article>


  
    <article id="post-nio/15.Mina系列一之入门" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-meta">
    <a href="/2016/01/18/nio/15.Mina系列一之入门/" class="article-date">
  <time datetime="2016-01-18T03:09:00.000Z" itemprop="datePublished">2016-01-18</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/categories/nio/">nio</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2016/01/18/nio/15.Mina系列一之入门/">Mina系列一之入门</a>
    </h1>
  

      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <h2 id="u7B80_u4ECB"><a href="#u7B80_u4ECB" class="headerlink" title="简介"></a>简介</h2><p>　　Mina是apache出品，作为应用程序 (客户端应用或者服务器端应用) 和基础网络层之间的粘合剂，可以基于 TCP、UDP、in-VM 通信甚至一个客户端的 RS-232C 串行协议，下图显示了 Mina 内部的实现细节，这正是每个 Mina 组件做的事情：<img src="/images/mina-architecture.png" alt="mina架构图"><br>　　概括来讲，基于 MINA 的应用划分为三个层次：<br>　　1. Service：具体 I/O 操作，比如处理客户端连接、断开、异常和io读写<br>　　2. Filter Chain：将字节过滤/转换为想要的数据结构。反之亦然<br>　　3. Handler：处理器，实现具体的业务逻辑<br>　　因此，要想创建一个基于 MINA 的应用，需要以下三步：<br>　　1. 创建一个 service - 从已存在的可用 service (*Acceptor) 中挑选一个或者创建你自己的<br>　　2. 创建一个 Filter Chain - 从现有 Filter 中挑选，或者创建一个用于转换请求/响应的自定义 Filter<br>　　3. 创建一个 Handler - 处理不同消息时编写具体业务逻辑</p>
<h2 id="u670D_u52A1_u7AEF"><a href="#u670D_u52A1_u7AEF" class="headerlink" title="服务端"></a>服务端</h2><p>　　从本质上说，服务器端监听一个端口以获得连入的请求，将其进行处理然后发送回复。服务器端还会为每个客户端 (无论是基于 TCP 还是基于 UDP 协议的) 创建并维护一个 session，如下图所示：<img src="/images/mina-server.png" alt="服务端处理"><br>　　1. I/O Acceptor：监听网络以获取连入的连接或者包<br>　　2. 对于一个新的连接，一个新的 session 会被创建（只要一个客户端连接到了 MINA 服务器端，就要创建一个新的 session 以存放持久化数据。即使协议还没连接上，也要创建），之后所有来自该 IP 地址/端口号组合的请求会在同一 session 中处理<br>　　3. 在一个 session 中接收到的所有包，将穿越上图中所示的 Filter Chain (过滤器链)。过滤器可以被用于修正包的内容 (比如转化为对象，添加或者删除信息等等)。对于从原始字节到高层对象的相互转换，PacketEncoder/Decoder 相当有用。<br>　　4. 包或转化来的对象最终交给 IOHandler。IOHandler 可以用于实现各种具体业务需求。</p>
<h2 id="u5BA2_u6237_u7AEF"><a href="#u5BA2_u6237_u7AEF" class="headerlink" title="客户端"></a>客户端</h2><p>　　客户端处理流程如下图所示：<img src="/images/mina-server.png" alt="服务端处理"><br>　　1. 客户端首先创建一个 IOConnector (用以连接套接字的 MINA 结构)，开启一个服务器的绑定<br>　　2. 在连接创建时，一个 session 会被创建并关联到该连接<br>　　3. 应用或者客户端写入 session，导致数据穿越 Filter Chain (过滤器链) 后被发送给服务器端<br>　　4. 所有接收自服务器端的响应或者消息穿越 Filter Chain (过滤器链) 后由 IOHandler 接收并处理</p>
<h2 id="u57FA_u672C_u7C7B"><a href="#u57FA_u672C_u7C7B" class="headerlink" title="基本类"></a>基本类</h2><p>　　1. IoAccepter 相当于网络应用程序中的服务器端<br>　　2. IoConnector 相当于客户端<br>　　3. IoSession 当前客户端到服务器端的一个连接实例<br>　　4. IoHandler 业务处理逻辑<br>　　5. IoFilter 过滤器用于悬接通讯层接口与业务层接口</p>
<h2 id="u53C2_u8003_u94FE_u63A5"><a href="#u53C2_u8003_u94FE_u63A5" class="headerlink" title="参考链接"></a>参考链接</h2><p><a href="http://mina.apache.org/mina-project/quick-start-guide.html" target="_blank" rel="external">http://mina.apache.org/mina-project/quick-start-guide.html</a></p>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://yoursite.com/2016/01/18/nio/15.Mina系列一之入门/" data-id="cijzzpv7l005u1cfsbwlqcw50" class="article-share-link">Share</a>
      
      
  <ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/mina/">mina</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/nio/">nio</a></li></ul>

    </footer>
  </div>
  
</article>


  
    <article id="post-database/4.分布式事务" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-meta">
    <a href="/2016/01/17/database/4.分布式事务/" class="article-date">
  <time datetime="2016-01-17T14:18:00.000Z" itemprop="datePublished">2016-01-17</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/categories/database/">database</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2016/01/17/database/4.分布式事务/">分布式事务</a>
    </h1>
  

      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <h2 id="u672C_u5730_u4E8B_u52A1"><a href="#u672C_u5730_u4E8B_u52A1" class="headerlink" title="本地事务"></a>本地事务</h2><p>　　以支付宝转账余额宝为例，假设有支付宝账户表：A（id，userId，amount），余额宝账户表：B（id，userId，amount），用户的userId=1；从支付宝转账1万块钱到余额宝的动作分为两步：<br>1）支付宝表扣除1万：update A set amount=amount-10000 where userId=1;<br>2）余额宝表增加1万：update B set amount=amount+10000 where userId=1;<br>　　如何确保支付宝余额宝收支平衡呢，有人说这个很简单嘛，可以用事务解决。<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="operator"><span class="keyword">Begin</span> <span class="keyword">transaction</span></span><br><span class="line">         <span class="keyword">update</span> A setamount=amount-<span class="number">10000</span><span class="keyword">where</span> userId=<span class="number">1</span>;</span></span><br><span class="line">         <span class="operator"><span class="keyword">update</span> B setamount=amount+<span class="number">10000</span><span class="keyword">where</span> userId=<span class="number">1</span>;</span></span><br><span class="line"><span class="operator"><span class="keyword">End</span> <span class="keyword">transaction</span></span><br><span class="line"><span class="keyword">commit</span>;</span></span><br></pre></td></tr></table></figure></p>
<p>　　非常正确，如果你使用spring的话一个注解就能搞定上述事务功能。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="annotation">@Transactional</span>(rollbackFor=Exception.class)</span><br><span class="line">publicvoidupdate() &#123;</span><br><span class="line">    updateATable(); <span class="comment">//更新A表</span></span><br><span class="line">    updateBTable(); <span class="comment">//更新B表</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>　　如果系统规模较小，数据表都在一个数据库实例上，上述本地事务方式可以很好地运行，但是如果系统规模较大，比如支付宝账户表和余额宝账户表显然不会在同一个数据库实例上，他们往往分布在不同的物理节点上，这时本地事务已经失去用武之地。既然本地事务失效，分布式事务自然就登上舞台。</p>
<h2 id="u4E24_u9636_u6BB5_u63D0_u4EA4_u534F_u8BAE"><a href="#u4E24_u9636_u6BB5_u63D0_u4EA4_u534F_u8BAE" class="headerlink" title="两阶段提交协议"></a>两阶段提交协议</h2><p>　　两阶段提交协议（Two-phase Commit，2PC）经常被用来实现分布式事务。一般分为协调器C和若干事务执行者Si两种角色，这里的事务执行者就是具体的数据库，协调器可以和事务执行器在一台机器上。<img src="/images/j20l908xta6.jpeg" alt="j20l908xta6.jpeg"><br>　　1. 我们的应用程序（client）发起一个开始请求到TC；<br>　　2. TC先将<prepare>消息写到本地日志，之后向所有的Si发起<prepare>消息。以支付宝转账到余额宝为例，TC给A的prepare消息是通知支付宝数据库相应账目扣款1万，TC给B的prepare消息是通知余额宝数据库相应账目增加1w。为什么在执行任务前需要先写本地日志，主要是为了故障后恢复用，本地日志起到现实生活中凭证的效果，如果没有本地日志（凭证），出问题容易死无对证；<br>　　3. Si收到<prepare>消息后，执行具体本机事务，但不会进行commit，如果成功返回<yes>，不成功返回<no>。同理，返回前都应把要返回的消息写到日志里，当作凭证。<br>　　4. TC收集所有执行器返回的消息，如果所有执行器都返回yes，那么给所有执行器发生送commit消息，执行器收到commit后执行本地事务的commit操作；如果有任一个执行器返回no，那么给所有执行器发送abort消息，执行器收到abort消息后执行事务abort操作。<br>　　注：TC或Si把发送或接收到的消息先写到日志里，主要是为了故障后恢复用。如某一Si从故障中恢复后，先检查本机的日志，如果已收到<commit>，则提交，如果<abort>则回滚。如果是<yes>，则再向TC询问一下，确定下一步。如果什么都没有，则很可能在<prepare>阶段Si就崩溃了，因此需要回滚。<br>　　现如今实现基于两阶段提交的分布式事务也没那么困难了，如果使用java，那么可以使用开源软件atomikos(<a href="http://www.atomikos.com/)来快速实现。" target="_blank" rel="external">http://www.atomikos.com/)来快速实现。</a><br>　　不过但凡使用过的上述两阶段提交的同学都可以发现性能实在是太差，根本不适合高并发的系统。为什么？<br>1）两阶段提交涉及多次节点间的网络通信，通信时间太长！<br>2）事务时间相对于变长了，锁定的资源的时间也变长了，造成资源等待时间也增加好多！<br>　　正是由于分布式事务存在很严重的性能问题，大部分高并发服务都在避免使用，往往通过其他途径来解决数据一致性问题。</prepare></yes></abort></commit></no></yes></prepare></prepare></prepare></p>
<h2 id="u4F7F_u7528_u6D88_u606F_u961F_u5217_u6765_u907F_u514D_u5206_u5E03_u5F0F_u4E8B_u52A1"><a href="#u4F7F_u7528_u6D88_u606F_u961F_u5217_u6765_u907F_u514D_u5206_u5E03_u5F0F_u4E8B_u52A1" class="headerlink" title="使用消息队列来避免分布式事务"></a>使用消息队列来避免分布式事务</h2><p>　　如果仔细观察生活的话，生活的很多场景已经给了我们提示。比如在北京很有名的姚记炒肝点了炒肝并付了钱后，他们并不会直接把你点的炒肝给你，而是给你一张小票，然后让你拿着小票到出货区排队去取。为什么他们要将付钱和取货两个动作分开呢？原因很多，其中一个很重要的原因是为了使他们接待能力增强（并发量更高）。<br>　　还是回到我们的问题，只要这张小票在，你最终是能拿到炒肝的。同理转账服务也是如此，当支付宝账户扣除1万后，我们只要生成一个凭证（消息）即可，这个凭证（消息）上写着“让余额宝账户增加 1万”，只要这个凭证（消息）能可靠保存，我们最终是可以拿着这个凭证（消息）让余额宝账户增加1万的，即我们能依靠这个凭证（消息）完成最终一致性。</p>
<h4 id="u5982_u4F55_u53EF_u9760_u4FDD_u5B58_u51ED_u8BC1_uFF08_u6D88_u606F_uFF09"><a href="#u5982_u4F55_u53EF_u9760_u4FDD_u5B58_u51ED_u8BC1_uFF08_u6D88_u606F_uFF09" class="headerlink" title="如何可靠保存凭证（消息）"></a>如何可靠保存凭证（消息）</h4><p>　　有两种方法：</p>
<h6 id="u4E1A_u52A1_u4E0E_u6D88_u606F_u8026_u5408_u7684_u65B9_u5F0F"><a href="#u4E1A_u52A1_u4E0E_u6D88_u606F_u8026_u5408_u7684_u65B9_u5F0F" class="headerlink" title="业务与消息耦合的方式"></a>业务与消息耦合的方式</h6><p>　　支付宝在完成扣款的同时，同时记录消息数据，这个消息数据与业务数据保存在同一数据库实例里（消息记录表表名为message）。<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="operator"><span class="keyword">Begin</span> <span class="keyword">transaction</span></span><br><span class="line">         <span class="keyword">update</span> A setamount=amount-<span class="number">10000</span><span class="keyword">where</span> userId=<span class="number">1</span>;</span></span><br><span class="line">         <span class="operator"><span class="keyword">insert</span> <span class="keyword">into</span> message(userId, amount,<span class="keyword">status</span>) <span class="keyword">values</span>(<span class="number">1</span>, <span class="number">10000</span>, <span class="number">1</span>);</span></span><br><span class="line"><span class="operator"><span class="keyword">End</span> <span class="keyword">transaction</span></span><br><span class="line"><span class="keyword">commit</span>;</span></span><br></pre></td></tr></table></figure></p>
<p>　　上述事务能保证只要支付宝账户里被扣了钱，消息一定能保存下来。当上述事务提交成功后，我们通过实时消息服务将此消息通知余额宝，余额宝处理成功后发送回复成功消息，支付宝收到回复后删除该条消息数据。</p>
<h6 id="u4E1A_u52A1_u4E0E_u6D88_u606F_u89E3_u8026_u65B9_u5F0F"><a href="#u4E1A_u52A1_u4E0E_u6D88_u606F_u89E3_u8026_u65B9_u5F0F" class="headerlink" title="业务与消息解耦方式"></a>业务与消息解耦方式</h6><p>　　上述保存消息的方式使得消息数据和业务数据紧耦合在一起，从架构上看不够优雅，而且容易诱发其他问题。为了解耦，可以采用以下方式。<br>　　1. <strong>支付宝在扣款事务提交之前</strong>，向实时消息服务请求发送消息，实时消息服务只记录消息数据，而不真正发送；<br>　　2. <strong>支付宝扣款事务提交成功后</strong>，向实时消息服务确认发送。实时消息服务得到确认发送指令后，才真正发送该消息；<br>　　3. <strong>支付宝扣款事务提交失败回滚后</strong>，向实时消息服务取消发送。实时消息服务得到取消发送指令后，该消息将会被取消；<br>　　4. 对于未确认的消息或取消的消息，需要有一个消息状态确认系统定时去支付宝系统查询这个消息的状态并进行更新。为什么需要这一步骤，举个例子：假设在第2步支付宝扣款事务被成功提交后，系统挂了，此时消息状态并未被更新为“确认发送”，从而导致消息不能被发送。<br>优点：消息数据独立存储，降低业务系统与消息系统间的耦合；<br>缺点：一次消息发送需要两次请求；业务处理服务需要实现消息状态回查接口。</p>
<h4 id="u5982_u4F55_u89E3_u51B3_u6D88_u606F_u91CD_u590D_u6295_u9012_u7684_u95EE_u9898"><a href="#u5982_u4F55_u89E3_u51B3_u6D88_u606F_u91CD_u590D_u6295_u9012_u7684_u95EE_u9898" class="headerlink" title="如何解决消息重复投递的问题"></a>如何解决消息重复投递的问题</h4><p>　　为什么相同的消息会被重复投递？比如余额宝处理完消息msg后，发送了处理成功的消息给支付宝，正常情况下支付宝应该要删除消息msg，但如果支付宝这时候悲剧的挂了，重启后一看消息msg还在，就会继续发送消息msg。<br>　　解决方法很简单，在余额宝这边<strong>增加消息应用状态表（message_apply），通俗来说就是个账本，用于记录消息的消费情况</strong>，每次来一个消息，在真正执行之前，先去消息应用状态表中查询一遍，如果找到说明是重复消息，丢弃即可，如果没找到才执行，同时插入到消息应用状态表（同一事务）。<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">for each msg in queue</span><br><span class="line">  <span class="operator"><span class="keyword">Begin</span> <span class="keyword">transaction</span></span><br><span class="line">    <span class="keyword">select</span> <span class="keyword">count</span>(*) <span class="keyword">as</span> cnt <span class="keyword">from</span> message_apply <span class="keyword">where</span> msg_id=msg.msg_id;</span></span><br><span class="line">    if cnt==0 then</span><br><span class="line">      <span class="operator"><span class="keyword">update</span> B <span class="keyword">set</span> amount=amount+<span class="number">10000</span> <span class="keyword">where</span> userId=<span class="number">1</span>;</span></span><br><span class="line">      <span class="operator"><span class="keyword">insert</span> <span class="keyword">into</span> message_apply(msg_id) <span class="keyword">values</span>(msg.msg_id);</span></span><br><span class="line">  <span class="operator"><span class="keyword">End</span> <span class="keyword">transaction</span></span><br><span class="line">  <span class="keyword">commit</span>;</span></span><br></pre></td></tr></table></figure></p>
<p>　　ebay的研发人员其实在2008年就提出了应用消息状态确认表来解决消息重复投递的问题：<a href="http://queue.acm.org/detail.cfm?id=1394128。" target="_blank" rel="external">http://queue.acm.org/detail.cfm?id=1394128。</a></p>
<h2 id="u53C2_u8003_u94FE_u63A5"><a href="#u53C2_u8003_u94FE_u63A5" class="headerlink" title="参考链接"></a>参考链接</h2><p><a href="http://blog.jobbole.com/89140/" target="_blank" rel="external">http://blog.jobbole.com/89140/</a></p>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://yoursite.com/2016/01/17/database/4.分布式事务/" data-id="cijzzpvhg00gk1cfsq5bptvsl" class="article-share-link">Share</a>
      
      
  <ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/database/">database</a></li></ul>

    </footer>
  </div>
  
</article>


  
    <article id="post-database/3.neo4j入门" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-meta">
    <a href="/2016/01/17/database/3.neo4j入门/" class="article-date">
  <time datetime="2016-01-17T13:50:00.000Z" itemprop="datePublished">2016-01-17</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/categories/database/">database</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2016/01/17/database/3.neo4j入门/">neo4j入门</a>
    </h1>
  

      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <h2 id="Windows_u5B89_u88C5"><a href="#Windows_u5B89_u88C5" class="headerlink" title="Windows安装"></a>Windows安装</h2><p>　　1. 启动服务<br>$ cd neo4j-community-2.2.5<br>$ bin\Neo4j.bat console  (注意，测试发现不可进入bin目录后再执行Neo4j.bat console)</p>
<p>Starting Neo4j Server…WARNING: not changing user<br>process [70299]… waiting for server to be ready…… OK.<br><a href="http://localhost:7474/" target="_blank" rel="external">http://localhost:7474/</a> is ready.</p>
<p>　　2. 默认登录名和密码：neo4j/neo4j：如果忘记密码，则需删除文件 data/dbms/authorization.<br>　　3. 注册成开机自动启动的服务：bin\Neo4jInstaller.bat install<br>　　只能通过localhost:7474访问，不能通过ip:port访问，why????</p>
<h2 id="u6982_u5FF5"><a href="#u6982_u5FF5" class="headerlink" title="概念"></a>概念</h2><p>　　Labels：给node分配标签，nodes根据label进行分组<br>　　Relationships: 有方向，类型，属性</p>
<h2 id="Cypher"><a href="#Cypher" class="headerlink" title="Cypher"></a>Cypher</h2><p>　　进入如下所示的学习路径：<img src="/images/cypher.png" alt="cypher.png"></p>
<h4 id="CREATE"><a href="#CREATE" class="headerlink" title="CREATE"></a>CREATE</h4><p>　　CREATE (ee:Person { name: “Emil”, from: “Sweden”, klout: 99 })<br>　　- (): 声明为一个node<br>　　- ee:Person 变量ee和标签Person赋予新node，可以声明多个标签（label）形如：ee:Person:Man的形式<br>　　- {} 给node添加属性</p>
<h4 id="MATCH"><a href="#MATCH" class="headerlink" title="MATCH"></a>MATCH</h4><p>　　查询表示Emil的节点：MATCH (ee:Person) WHERE ee.name = “Emil” RETURN ee;<br>　　- MATCH 指定一个关于nodes 和 relationships 的模式<br>　　- (ee:Person) 根据标签（Label）获取单个节点模式，并将该节点分配给变量ee<br>　　- WHERE 限制返回结果<br>　　- ee.name = “Emil” name属性值为Emil<br>　　- RETURN 请求指定的返回结果<br>　　等价于：MATCH (tom {name: “Emil”}) RETURN tom</p>
<p>　　返回前10个人：MATCH (people:Person) RETURN people.name LIMIT 10</p>
<p>　　查询90年代发行的电影：MATCH (nineties:Movie) WHERE nineties.released &gt; 1990 AND nineties.released &lt; 2000 RETURN nineties.title</p>
<p>　　查询Tom Hanks演过的电影：MATCH (tom:Person {name: “Tom Hanks”})-[:ACTED_IN]-&gt;(tomHanksMovies) RETURN tom,tomHanksMovies</p>
<p>　　查询导演了Cloud Atlas（云图）的人<br>MATCH (cloudAtlas {title: “Cloud Atlas”})&lt;-[:DIRECTED]-(directors) RETURN directors.name<br>MATCH (directors)-[:DIRECTED]-&gt;(cloudAtlas {title: “Cloud Atlas”}) RETURN directors.name</p>
<p>　　查询和Tom Hanks参演了相同电影的人<br>MATCH (tom:Person {name:”Tom Hanks”})-[:ACTED_IN]-&gt;(m)&lt;-[:ACTED_IN]-(coActors) RETURN coActors.name</p>
<p>　　查询与Cloud Atlas（云图）有关的人，包括导演，演员等。<br>MATCH (people:Person)-[relatedTo]-(:Movie {title: “Cloud Atlas”}) RETURN people.name, Type(relatedTo), relatedTo<br><img src="/images/match-1.png" alt="match-1.png"><img src="/images/match-2.png" alt="match-2.png"></p>
<p>　　查询和Kevin Bacon的1到4度关系的电影和演员<br>MATCH (bacon:Person {name:”Kevin Bacon”})-[*1..4]-(hollywood)  RETURN DISTINCT hollywood</p>
<p>　　查询Kevin Bacon和Meg Ryan之间的最短路径<br>MATCH p=shortestPath(<br>  (bacon:Person {name:”Kevin Bacon”})-[*]-(meg:Person {name:”Meg Ryan”})<br>)<br>RETURN p<br><img src="/images/match-3.png" alt="match-3.png"></p>
<p>　　查询和Tom Hanks未合作过，但是和Tom Hanks的合作者合作过的人<br>MATCH (tom:Person {name:”Tom Hanks”})-[:ACTED_IN]-&gt;(m)&lt;-[:ACTED_IN]-(coActors),<br>      (coActors)-[:ACTED_IN]-&gt;(m2)&lt;-[:ACTED_IN]-(cocoActors)<br>WHERE NOT (tom)-[:ACTED_IN]-&gt;(m2)<br>RETURN cocoActors.name AS Recommended, count(*) AS Strength ORDER BY Strength DESC</p>
<p>　　查找能够把Tom Hanks介绍给Tom Cruise的人<br>MATCH (tom:Person {name:”Tom Hanks”})-[:ACTED_IN]-&gt;(m)&lt;-[:ACTED_IN]-(coActors),<br>      (coActors)-[:ACTED_IN]-&gt;(m2)&lt;-[:ACTED_IN]-(cruise:Person {name:”Tom Cruise”})<br>RETURN tom, m, coActors, m2, cruise<br>　　请根据以下分析上面的两条语句：<img src="/images/match-4.png" alt="match-4.png"></p>
<p>　　删除所有节点<br>MATCH (n) OPTIONAL MATCH (n)-[r1]-() DELETE n,r1<br>　　查询所有节点<br>MATCH (n) RETURN n<br>MATCH (n {name:”A”})-[r1]-() DELETE n,r1</p>
<p>　　删除指定的关系<br>MATCH (n) OPTIONAL MATCH (n)-[r:IN {name:”A-M1”} ]-() DELETE r<br>　　等价于MATCH (n)-[r:IN {name:”A-M2”} ]-() DELETE r<br>　　同时删除多个关系<br>MATCH (n)-[r:IN]-() where r.name=”A-M2” or r.name=”A-M1” DELETE r</p>
<h2 id="u81EA_u5EFA_u6570_u636E"><a href="#u81EA_u5EFA_u6570_u636E" class="headerlink" title="自建数据"></a>自建数据</h2><p>CREATE (A:test {name:”A”}),(B:test {name:”B”}),(C:test {name:”C”}),(D:test {name:”D”}),<br> (M1:M {name:”M1”}),(M2:M {name:”M2”}),(M3:M {name:”M3”}),(M4:M {name:”M4”}),<br>    (A)-[:IN {name:”A-M1”}]-&gt;(M1),<br> (A)-[:IN {name:”A-M2”}]-&gt;(M2),<br> (B)-[:IN]-&gt;(M1),<br> (B)-[:IN]-&gt;(M2),<br> (B)-[:IN]-&gt;(M3),<br> (C)-[:IN]-&gt;(M1),<br> (C)-[:IN]-&gt;(M3),<br>(D)-[:IN]-&gt;(M3),<br> (D)-[:IN]-&gt;(M4)</p>
<p>查询与A关联的M，输出M1, M2<br>MATCH (tom:test {name:”A”})-[:IN]-&gt;(m) return m</p>
<p>查询带有自定关系的节点<br>MATCH (n)-[ {name:”A-M1”} ]-&gt;(xx) return xx<br>MATCH (n)-[:IN {name:”A-M1”} ]-&gt;(xx) return xx</p>
<p>输出B、C，注意，C只关联了M1，未关联M2，也输出了，但是未输出A(A作为了左侧的查询条件)<br>MATCH (tom:test {name:”A”})-[:IN]-&gt;(m)&lt;-[:IN]-(coActors) return coActors</p>
<p>输出M1 M2 M3<br>MATCH (tom:test {name:”A”})-[:IN]-&gt;(m)&lt;-[:IN]-(coActors),<br>      (coActors)-[:IN]-&gt;(m2)&lt;-[:IN]-(cocoActors)<br>RETURN m2</p>
<p>输出A、B、C、D，coActors的值为B、C，为何cocoActors的输出值没有排除B、C<br>MATCH (tom:test {name:”A”})-[:IN]-&gt;(m)&lt;-[:IN]-(coActors),<br>      (coActors)-[:IN]-&gt;(m2)&lt;-[:IN]-(cocoActors)<br>RETURN cocoActors</p>
<p>输出M3<br>MATCH (tom:test {name:”A”})-[:IN]-&gt;(m)&lt;-[:IN]-(coActors),<br>      (coActors)-[:IN]-&gt;(m2)&lt;-[:IN]-(cocoActors)<br>WHERE NOT (tom)-[:IN]-&gt;(m2)<br>RETURN m2</p>
<p>输出B、C、D<br>MATCH (tom:test {name:”A”})-[:IN]-&gt;(m)&lt;-[:IN]-(coActors),<br>      (coActors)-[:IN]-&gt;(m2)&lt;-[:IN]-(cocoActors)<br>WHERE NOT (tom)-[:IN]-&gt;(m2)<br>RETURN cocoActors</p>
<p>查询和A未合作过，但是和A的合作者合作过的人?????????<br>MATCH (tom:test {name:”A”})-[:IN]-&gt;(m)&lt;-[:IN]-(coActors),<br>      (coActors)-[:IN]-&gt;(m2)&lt;-[:IN]-(cocoActors)<br>WHERE NOT (tom)-[:IN]-&gt;(m2)<br>RETURN cocoActors.name AS Recommended, count(*) AS Strength ORDER BY Strength DESC</p>
<p>删除所有标签为Person和Movie的节点<br>MATCH (a:Person),(m:Movie) OPTIONAL MATCH (a)-[r1]-(), (m)-[r2]-() DELETE a,r1,m,r2</p>
<p>查询所有的节点<br>MATCH (n) RETURN n</p>
<h2 id="CREATE_MORE"><a href="#CREATE_MORE" class="headerlink" title="CREATE MORE"></a>CREATE MORE</h2><p>MATCH (ee:Person) WHERE ee.name = “Emil”<br>CREATE (js:Person { name: “Johan”, from: “Sweden”, learn: “surfing” }),<br>(ir:Person { name: “Ian”, from: “England”, title: “author” }),<br>(rvb:Person { name: “Rik”, from: “Belgium”, pet: “Orval” }),<br>(ally:Person { name: “Allison”, from: “California”, hobby: “surfing” }),<br>(ee)-[:KNOWS {since: 2001}]-&gt;(js),(ee)-[:KNOWS {rating: 5}]-&gt;(ir),<br>(js)-[:KNOWS]-&gt;(ir),(js)-[:KNOWS]-&gt;(rvb),<br>(ir)-[:KNOWS]-&gt;(js),(ir)-[:KNOWS]-&gt;(ally),<br>(rvb)-[:KNOWS]-&gt;(ally)</p>
<h2 id="Pattern_matching"><a href="#Pattern_matching" class="headerlink" title="Pattern matching"></a>Pattern matching</h2><p>MATCH (ee:Person)-[:KNOWS]-(friends) WHERE ee.name = “Emil” RETURN ee, friends</p>
<ul>
<li>MATCH 描述了从已知node查询nodes的模式</li>
<li>(ee)starts the pattern with a Person (qualified by WHERE)</li>
<li>-[:KNOWS]-匹配”KNOWS” relationships (in either direction)</li>
<li>(friends)存储Emil的朋友</li>
</ul>
<h2 id="Recommend__u63A8_u8350"><a href="#Recommend__u63A8_u8350" class="headerlink" title="Recommend 推荐"></a>Recommend 推荐</h2><p>　　Johan 将要学习 surf, 所有他也想找一个学习surfing的朋友:</p>
<p>MATCH (js:Person)-[:KNOWS]-()-[:KNOWS]-(surfer)<br>WHERE js.name = “Johan” AND surfer.hobby = “surfing”<br>RETURN DISTINCT surfer</p>
<ul>
<li>()圆括号忽略nodes</li>
<li>DISTINCT可能有多个路径匹配模式</li>
<li>surferwill contain Allison, a friend of a friend who surfs<h2 id="u53C2_u8003_u94FE_u63A5"><a href="#u53C2_u8003_u94FE_u63A5" class="headerlink" title="参考链接"></a>参考链接</h2></li>
</ul>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://yoursite.com/2016/01/17/database/3.neo4j入门/" data-id="cijzzpvhk00gn1cfsm7qev8qa" class="article-share-link">Share</a>
      
      
  <ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/postgresql/">postgresql</a></li></ul>

    </footer>
  </div>
  
</article>


  
    <article id="post-database/2.postgresql入门" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-meta">
    <a href="/2016/01/17/database/2.postgresql入门/" class="article-date">
  <time datetime="2016-01-17T13:28:00.000Z" itemprop="datePublished">2016-01-17</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/categories/database/">database</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2016/01/17/database/2.postgresql入门/">postgresql入门</a>
    </h1>
  

      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <h2 id="u767E_u4E07_u7EA7_u522B_u6570_u636E_u5BFC_u51FA"><a href="#u767E_u4E07_u7EA7_u522B_u6570_u636E_u5BFC_u51FA" class="headerlink" title="百万级别数据导出"></a>百万级别数据导出</h2><p>　　1. 直接查询所有的数据导出，在ResultSet rs = st.executeQuery（）执行后内存溢出，分析得知，所有的查询结果都会加载到ResultSet，百万级的数据，直接溢出<br>　　2. 导出时，不进行任何order操作，直接全表导出。如果采用关键字limit offset分页查询，每页2000条记录，在前几百页，性能不错。越往后面，速度越慢，几乎达到了每次查询耗时一秒的消耗。<br>　　3. 导出时，进行order操作，即使order字段上建立了索引，采用statement，后期很慢，甚至出现卡死情况（测试了多次，分页达到600左右的时候出现问题）。<br>　　4. 导出时，进行order操作，即使order字段上建立了索引，采用PrepareStatement，后期仍然很慢，但是没有出现卡死的情况（只测试了一次）。耗时约40分钟。<br>　　5. 每次翻页的时候记录一下最后一条记录的id号，然后select top 10 * form tablename where id&gt;xxx，这样即使数据量再大也不怕（未测试）</p>
<h2 id="u6811_u5F62_uFF08_u9012_u5F52_uFF09_u67E5_u8BE2"><a href="#u6811_u5F62_uFF08_u9012_u5F52_uFF09_u67E5_u8BE2" class="headerlink" title="树形（递归）查询"></a>树形（递归）查询</h2><p>　　测试如下：<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="operator"><span class="keyword">create</span> <span class="keyword">table</span> tb(<span class="keyword">id</span> <span class="built_in">varchar</span>(<span class="number">3</span>) , pid <span class="built_in">varchar</span>(<span class="number">3</span>) , <span class="keyword">name</span> <span class="built_in">varchar</span>(<span class="number">10</span>));</span> </span><br><span class="line"><span class="operator"><span class="keyword">insert</span> <span class="keyword">into</span> tb <span class="keyword">values</span>(<span class="string">'002'</span> , <span class="number">0</span> , <span class="string">'浙江省'</span>);</span> </span><br><span class="line"><span class="operator"><span class="keyword">insert</span> <span class="keyword">into</span> tb <span class="keyword">values</span>(<span class="string">'001'</span> , <span class="number">0</span> , <span class="string">'广东省'</span>);</span> </span><br><span class="line"><span class="operator"><span class="keyword">insert</span> <span class="keyword">into</span> tb <span class="keyword">values</span>(<span class="string">'003'</span> , <span class="string">'002'</span> , <span class="string">'衢州市'</span>);</span>  </span><br><span class="line"><span class="operator"><span class="keyword">insert</span> <span class="keyword">into</span> tb <span class="keyword">values</span>(<span class="string">'004'</span> , <span class="string">'002'</span> , <span class="string">'杭州市'</span>) ;</span> </span><br><span class="line"><span class="operator"><span class="keyword">insert</span> <span class="keyword">into</span> tb <span class="keyword">values</span>(<span class="string">'005'</span> , <span class="string">'002'</span> , <span class="string">'湖州市'</span>);</span>  </span><br><span class="line"><span class="operator"><span class="keyword">insert</span> <span class="keyword">into</span> tb <span class="keyword">values</span>(<span class="string">'006'</span> , <span class="string">'002'</span> , <span class="string">'嘉兴市'</span>) ;</span> </span><br><span class="line"><span class="operator"><span class="keyword">insert</span> <span class="keyword">into</span> tb <span class="keyword">values</span>(<span class="string">'007'</span> , <span class="string">'002'</span> , <span class="string">'宁波市'</span>);</span>  </span><br><span class="line"><span class="operator"><span class="keyword">insert</span> <span class="keyword">into</span> tb <span class="keyword">values</span>(<span class="string">'008'</span> , <span class="string">'002'</span> , <span class="string">'绍兴市'</span>) ;</span> </span><br><span class="line"><span class="operator"><span class="keyword">insert</span> <span class="keyword">into</span> tb <span class="keyword">values</span>(<span class="string">'009'</span> , <span class="string">'002'</span> , <span class="string">'台州市'</span>);</span>  </span><br><span class="line"><span class="operator"><span class="keyword">insert</span> <span class="keyword">into</span> tb <span class="keyword">values</span>(<span class="string">'010'</span> , <span class="string">'002'</span> , <span class="string">'温州市'</span>) ;</span> </span><br><span class="line"><span class="operator"><span class="keyword">insert</span> <span class="keyword">into</span> tb <span class="keyword">values</span>(<span class="string">'011'</span> , <span class="string">'002'</span> , <span class="string">'丽水市'</span>);</span>  </span><br><span class="line"><span class="operator"><span class="keyword">insert</span> <span class="keyword">into</span> tb <span class="keyword">values</span>(<span class="string">'012'</span> , <span class="string">'002'</span> , <span class="string">'金华市'</span>) ;</span> </span><br><span class="line"><span class="operator"><span class="keyword">insert</span> <span class="keyword">into</span> tb <span class="keyword">values</span>(<span class="string">'013'</span> , <span class="string">'002'</span> , <span class="string">'舟山市'</span>);</span>  </span><br><span class="line"><span class="operator"><span class="keyword">insert</span> <span class="keyword">into</span> tb <span class="keyword">values</span>(<span class="string">'014'</span> , <span class="string">'004'</span> , <span class="string">'上城区'</span>) ;</span> </span><br><span class="line"><span class="operator"><span class="keyword">insert</span> <span class="keyword">into</span> tb <span class="keyword">values</span>(<span class="string">'015'</span> , <span class="string">'004'</span> , <span class="string">'下城区'</span>);</span>  </span><br><span class="line"><span class="operator"><span class="keyword">insert</span> <span class="keyword">into</span> tb <span class="keyword">values</span>(<span class="string">'016'</span> , <span class="string">'004'</span> , <span class="string">'拱墅区'</span>) ;</span> </span><br><span class="line"><span class="operator"><span class="keyword">insert</span> <span class="keyword">into</span> tb <span class="keyword">values</span>(<span class="string">'017'</span> , <span class="string">'004'</span> , <span class="string">'余杭区'</span>) ;</span> </span><br><span class="line"><span class="operator"><span class="keyword">insert</span> <span class="keyword">into</span> tb <span class="keyword">values</span>(<span class="string">'018'</span> , <span class="string">'011'</span> , <span class="string">'金东区'</span>) ;</span> </span><br><span class="line"><span class="operator"><span class="keyword">insert</span> <span class="keyword">into</span> tb <span class="keyword">values</span>(<span class="string">'019'</span> , <span class="string">'001'</span> , <span class="string">'广州市'</span>) ;</span> </span><br><span class="line"><span class="operator"><span class="keyword">insert</span> <span class="keyword">into</span> tb <span class="keyword">values</span>(<span class="string">'020'</span> , <span class="string">'001'</span> , <span class="string">'深圳市'</span>) ;</span></span><br></pre></td></tr></table></figure></p>
<p>　　测试语句，查询浙江省及以下县市：<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">with RECURSIVE cte as </span><br><span class="line">( </span><br><span class="line">    <span class="operator"><span class="keyword">select</span> a.<span class="keyword">id</span>,a.<span class="keyword">name</span>,a.pid <span class="keyword">from</span> tb a <span class="keyword">where</span> <span class="keyword">id</span>=<span class="string">'002'</span> </span><br><span class="line">    <span class="keyword">union</span> all  </span><br><span class="line">    <span class="keyword">select</span> <span class="keyword">k</span>.<span class="keyword">id</span>,<span class="keyword">k</span>.<span class="keyword">name</span>,<span class="keyword">k</span>.pid  <span class="keyword">from</span> tb <span class="keyword">k</span> <span class="keyword">inner</span> <span class="keyword">join</span> cte <span class="keyword">c</span> <span class="keyword">on</span> <span class="keyword">c</span>.<span class="keyword">id</span> = <span class="keyword">k</span>.pid </span><br><span class="line">)<span class="keyword">select</span> <span class="keyword">id</span>,<span class="keyword">name</span> <span class="keyword">from</span> cte;</span></span><br></pre></td></tr></table></figure></p>
<p>　　输出：<br> id  |  name<br>—–+——–<br> 002 | 浙江省<br> 003 | 衢州市<br> 004 | 杭州市<br> 005 | 湖州市<br> 006 | 嘉兴市<br> 007 | 宁波市<br> 008 | 绍兴市<br> 009 | 台州市<br> 010 | 温州市<br> 011 | 丽水市<br> 012 | 金华市<br> 013 | 舟山市<br> 014 | 上城区<br> 015 | 下城区<br> 016 | 拱墅区<br> 017 | 余杭区<br> 018 | 金东区<br>(17 rows)</p>
<p>　　如果想按层次分别显示出来，也可以这么写<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">with RECURSIVE cte as</span><br><span class="line">(</span><br><span class="line"><span class="operator"><span class="keyword">select</span> a.<span class="keyword">id</span>,<span class="keyword">cast</span>(a.<span class="keyword">name</span> <span class="keyword">as</span> <span class="built_in">varchar</span>(<span class="number">100</span>)) <span class="keyword">from</span> tb a <span class="keyword">where</span> <span class="keyword">id</span>=<span class="string">'002'</span></span><br><span class="line"><span class="keyword">union</span> all </span><br><span class="line"><span class="keyword">select</span> <span class="keyword">k</span>.<span class="keyword">id</span>,<span class="keyword">cast</span>(<span class="keyword">c</span>.<span class="keyword">name</span>||<span class="string">'&gt;'</span>||<span class="keyword">k</span>.<span class="keyword">name</span> <span class="keyword">as</span> <span class="built_in">varchar</span>(<span class="number">100</span>)) <span class="keyword">as</span> <span class="keyword">name</span>  <span class="keyword">from</span> tb <span class="keyword">k</span> <span class="keyword">inner</span> <span class="keyword">join</span> cte <span class="keyword">c</span> <span class="keyword">on</span> <span class="keyword">c</span>.<span class="keyword">id</span> = <span class="keyword">k</span>.pid</span><br><span class="line">)<span class="keyword">select</span> <span class="keyword">id</span>,<span class="keyword">name</span> <span class="keyword">from</span> cte ;</span></span><br></pre></td></tr></table></figure></p>
<p> id  |         name<br>—–+———————-<br> 002 | 浙江省<br> 003 | 浙江省&gt;衢州市<br> 004 | 浙江省&gt;杭州市<br> 005 | 浙江省&gt;湖州市<br> 006 | 浙江省&gt;嘉兴市<br> 007 | 浙江省&gt;宁波市<br> 008 | 浙江省&gt;绍兴市<br> 009 | 浙江省&gt;台州市<br> 010 | 浙江省&gt;温州市<br> 011 | 浙江省&gt;丽水市<br> 012 | 浙江省&gt;金华市<br> 013 | 浙江省&gt;舟山市<br> 014 | 浙江省&gt;杭州市&gt;上城区<br> 015 | 浙江省&gt;杭州市&gt;下城区<br> 016 | 浙江省&gt;杭州市&gt;拱墅区<br> 017 | 浙江省&gt;杭州市&gt;余杭区<br> 018 | 浙江省&gt;丽水市&gt;金东区</p>
<h2 id="u53C2_u8003_u94FE_u63A5"><a href="#u53C2_u8003_u94FE_u63A5" class="headerlink" title="参考链接"></a>参考链接</h2>
      
    </div>
    <footer class="article-footer">
      <a data-url="http://yoursite.com/2016/01/17/database/2.postgresql入门/" data-id="cijzzpvhn00gr1cfswqy2pusz" class="article-share-link">Share</a>
      
      
  <ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/postgresql/">postgresql</a></li></ul>

    </footer>
  </div>
  
</article>


  
    <article id="post-java/16.数据结构" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-meta">
    <a href="/2016/01/17/java/16.数据结构/" class="article-date">
  <time datetime="2016-01-17T13:21:37.000Z" itemprop="datePublished">2016-01-17</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/categories/java/">java</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2016/01/17/java/16.数据结构/">数据结构</a>
    </h1>
  

      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <h2 id="u5E38_u7528_u6570_u636E_u7ED3_u6784"><a href="#u5E38_u7528_u6570_u636E_u7ED3_u6784" class="headerlink" title="常用数据结构"></a>常用数据结构</h2><p>　　链表：一个链表由很多节点组成，每个节点包含表示内容的数据域和指向下一个节点的链域两部分。正是通过链域将节点连接形成一个表。围绕链表的基本操作有添加节点、删除节点和查找节点等。<br>　　堆栈：限定了只能从线性表的一端进行数据的存取，这一端称为栈顶，另一端为栈底。 入栈操作是先向栈顶方向移动一位，存入数据；出栈操作则是取出栈顶数据，然后向栈底移动一位。体现LIFO/FILO的思想。<br>　　队列：限定了数据只能从线性表的一端存入，从另一端取出。存入一端成为队尾，取出一端称为队首。体现LILO/FIFO的思想。<br>　　二叉树：树中的每个节点最多有两个子节点。这两个子节点分别称为左子节点和右子节点。在建立二叉搜索树时，要求一个节点的左子节点的关键字值小于这个节点而右子节点的关键字值大于或等于这个节点。常见的遍历操作有前序、中序和后序遍历。</p>
<h2 id="java_u4E2D_u5806_u6808"><a href="#java_u4E2D_u5806_u6808" class="headerlink" title="java中堆栈"></a>java中堆栈</h2><p>　　首先，堆栈是计算机语言中常用术语，堆栈是栈的俗称！比如在Java中我们常常说堆栈什么什么的，其实就是说栈内信息！此时有人就问：Java中明明有堆和栈两个概念呀？！不错，堆和栈的确是两种不同的内存操作单元，它们用途不同，但堆栈就是栈的俗称，你可以理解它其实就是栈！<br>　　相同点：都是RAM中存放数据的地方<br>　　不同点：<br>　　　　栈：栈具有数据结构中栈的特点，后进先出，存取速度快，但大小生命周期固定，主要应用于8个基本数据类型（byte,int,long,float,double,char,boolean）<br>　　　　堆：存取速度慢，但能动态分配内存，主要应用于对象（new方式建立）</p>
<h2 id="u53C2_u8003_u94FE_u63A5"><a href="#u53C2_u8003_u94FE_u63A5" class="headerlink" title="参考链接"></a>参考链接</h2><p>数据结构之链表：<a href="http://www.cnblogs.com/wing011203/archive/2013/04/09/3010985.html" target="_blank" rel="external">http://www.cnblogs.com/wing011203/archive/2013/04/09/3010985.html</a><br>数据结构之二叉树：<a href="http://www.cnblogs.com/wing011203/archive/2013/04/12/3016409.html" target="_blank" rel="external">http://www.cnblogs.com/wing011203/archive/2013/04/12/3016409.html</a></p>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://yoursite.com/2016/01/17/java/16.数据结构/" data-id="cijzzpve200db1cfsxe8gt7rp" class="article-share-link">Share</a>
      
      
  <ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/数据结构/">数据结构</a></li></ul>

    </footer>
  </div>
  
</article>


  
    <article id="post-java/15.FastDFS入门" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-meta">
    <a href="/2016/01/17/java/15.FastDFS入门/" class="article-date">
  <time datetime="2016-01-17T13:21:36.000Z" itemprop="datePublished">2016-01-17</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/categories/java/">java</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2016/01/17/java/15.FastDFS入门/">FastDFS入门</a>
    </h1>
  

      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <h2 id="u7B80_u4ECB"><a href="#u7B80_u4ECB" class="headerlink" title="简介"></a>简介</h2><p>　　1. 安装地址：<a href="https://github.com/happyfish100" target="_blank" rel="external">https://github.com/happyfish100</a><br>　　2. 安装模块：<img src="/images/fastdfs.png" alt="fastdfs.png"><br>　　3. 安装步骤：详见各自的INSTALL文件，如果出现错误，向作者反馈<br>　　4. 配置修改：<br>    /etc/fdfs/tracker.conf修改参数base_path指定路径<br>    /etc/fdfs/storage.conf修改参数base_path、store_path0、tracker_server<br>    如需进行c客户端的上传测试，/etc/fdfs/client.conf修改参数base_path、tracker_server</p>
<h2 id="u53C2_u8003_u94FE_u63A5"><a href="#u53C2_u8003_u94FE_u63A5" class="headerlink" title="参考链接"></a>参考链接</h2><p><a href="http://tech.uc.cn/?p=221" target="_blank" rel="external">http://tech.uc.cn/?p=221</a><br><a href="http://kb.cnblogs.com/page/82280/" target="_blank" rel="external">http://kb.cnblogs.com/page/82280/</a></p>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://yoursite.com/2016/01/17/java/15.FastDFS入门/" data-id="cijzzpve500de1cfszyh5om9d" class="article-share-link">Share</a>
      
      
  <ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/fastdfs/">fastdfs</a></li></ul>

    </footer>
  </div>
  
</article>


  
  
    <nav id="page-nav">
      <a class="extend prev" rel="prev" href="/page/2/">&laquo; Prev</a><a class="page-number" href="/">1</a><a class="page-number" href="/page/2/">2</a><span class="page-number current">3</span><a class="page-number" href="/page/4/">4</a><a class="page-number" href="/page/5/">5</a><span class="space">&hellip;</span><a class="page-number" href="/page/15/">15</a><a class="extend next" rel="next" href="/page/4/">Next &raquo;</a>
    </nav>
  
</section>
        
          <aside id="sidebar">
  
    
  <div class="widget-wrap">
    <h3 class="widget-title">Categories</h3>
    <div class="widget">
      <ul class="category-list"><li class="category-list-item"><a class="category-list-link" href="/categories/cache/">cache</a><span class="category-list-count">3</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/database/">database</a><span class="category-list-count">13</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/docker/">docker</a><span class="category-list-count">6</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/gis/">gis</a><span class="category-list-count">4</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/git/">git</a><span class="category-list-count">2</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/go/">go</a><span class="category-list-count">1</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/gradle/">gradle</a><span class="category-list-count">1</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/groovy/">groovy</a><span class="category-list-count">1</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/hadoop/">hadoop</a><span class="category-list-count">1</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/java/">java</a><span class="category-list-count">18</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/linux/">linux</a><span class="category-list-count">4</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/log/">log</a><span class="category-list-count">11</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/mongodb/">mongodb</a><span class="category-list-count">14</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/mq/">mq</a><span class="category-list-count">6</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/mybatis/">mybatis</a><span class="category-list-count">9</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/netty/">netty</a><span class="category-list-count">1</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/nginx/">nginx</a><span class="category-list-count">3</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/nio/">nio</a><span class="category-list-count">19</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/nodejs/">nodejs</a><span class="category-list-count">1</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/redis/">redis</a><span class="category-list-count">6</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/rpc/">rpc</a><span class="category-list-count">14</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/spring/">spring</a><span class="category-list-count">5</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/tomcat/">tomcat</a><span class="category-list-count">2</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/zookeeper/">zookeeper</a><span class="category-list-count">2</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/杂项/">杂项</a><span class="category-list-count">2</span></li></ul>
    </div>
  </div>

  
    
  <div class="widget-wrap">
    <h3 class="widget-title">Tags</h3>
    <div class="widget">
      <ul class="tag-list"><li class="tag-list-item"><a class="tag-list-link" href="/tags/aop/">aop</a><span class="tag-list-count">8</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/aspectj/">aspectj</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/cache/">cache</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/classloader/">classloader</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/common-loggings/">common-loggings</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/commonjs/">commonjs</a><span class="tag-list-count">4</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/database/">database</a><span class="tag-list-count">10</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/docker/">docker</a><span class="tag-list-count">6</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/dubbo/">dubbo</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/fastdfs/">fastdfs</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/gclib/">gclib</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/geohash/">geohash</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/gis/">gis</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/git/">git</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/go/">go</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/gradle/">gradle</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/groovy/">groovy</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/hadoop/">hadoop</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/hessisan/">hessisan</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/java/">java</a><span class="tag-list-count">48</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/javassist/">javassist</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/kafka/">kafka</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/linux/">linux</a><span class="tag-list-count">4</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/log/">log</a><span class="tag-list-count">11</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/log4j/">log4j</a><span class="tag-list-count">4</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/logback/">logback</a><span class="tag-list-count">5</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/map/">map</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/memecached/">memecached</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/mina/">mina</a><span class="tag-list-count">5</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/mongodb/">mongodb</a><span class="tag-list-count">14</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/mq/">mq</a><span class="tag-list-count">6</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/mqtt/">mqtt</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/mybatis/">mybatis</a><span class="tag-list-count">9</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/netty/">netty</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/nginx/">nginx</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/nio/">nio</a><span class="tag-list-count">5</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/nodejs/">nodejs</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/postgresql/">postgresql</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/protobuf/">protobuf</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/proxy/">proxy</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/push/">push</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/rabbitmq/">rabbitmq</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/redis/">redis</a><span class="tag-list-count">6</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/rpc/">rpc</a><span class="tag-list-count">14</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/sitemesh/">sitemesh</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/sld/">sld</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/slf4j/">slf4j</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/socket/">socket</a><span class="tag-list-count">7</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/spatialite-for-android/">spatialite for android</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/spi/">spi</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/spring/">spring</a><span class="tag-list-count">13</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/tomcat/">tomcat</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/transaction/">transaction</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/web/">web</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/xSocket/">xSocket</a><span class="tag-list-count">7</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/zbus/">zbus</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/zookeeper/">zookeeper</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/数据结构/">数据结构</a><span class="tag-list-count">2</span></li></ul>
    </div>
  </div>

  
    
  <div class="widget-wrap">
    <h3 class="widget-title">Tag Cloud</h3>
    <div class="widget tagcloud">
      <a href="/tags/aop/" style="font-size: 15.38px;">aop</a> <a href="/tags/aspectj/" style="font-size: 11.54px;">aspectj</a> <a href="/tags/cache/" style="font-size: 11.54px;">cache</a> <a href="/tags/classloader/" style="font-size: 10px;">classloader</a> <a href="/tags/common-loggings/" style="font-size: 10px;">common-loggings</a> <a href="/tags/commonjs/" style="font-size: 12.31px;">commonjs</a> <a href="/tags/database/" style="font-size: 16.92px;">database</a> <a href="/tags/docker/" style="font-size: 13.85px;">docker</a> <a href="/tags/dubbo/" style="font-size: 10px;">dubbo</a> <a href="/tags/fastdfs/" style="font-size: 10px;">fastdfs</a> <a href="/tags/gclib/" style="font-size: 10.77px;">gclib</a> <a href="/tags/geohash/" style="font-size: 10px;">geohash</a> <a href="/tags/gis/" style="font-size: 10px;">gis</a> <a href="/tags/git/" style="font-size: 10.77px;">git</a> <a href="/tags/go/" style="font-size: 10px;">go</a> <a href="/tags/gradle/" style="font-size: 10px;">gradle</a> <a href="/tags/groovy/" style="font-size: 10px;">groovy</a> <a href="/tags/hadoop/" style="font-size: 10px;">hadoop</a> <a href="/tags/hessisan/" style="font-size: 11.54px;">hessisan</a> <a href="/tags/java/" style="font-size: 20px;">java</a> <a href="/tags/javassist/" style="font-size: 10px;">javassist</a> <a href="/tags/kafka/" style="font-size: 11.54px;">kafka</a> <a href="/tags/linux/" style="font-size: 12.31px;">linux</a> <a href="/tags/log/" style="font-size: 17.69px;">log</a> <a href="/tags/log4j/" style="font-size: 12.31px;">log4j</a> <a href="/tags/logback/" style="font-size: 13.08px;">logback</a> <a href="/tags/map/" style="font-size: 10px;">map</a> <a href="/tags/memecached/" style="font-size: 10.77px;">memecached</a> <a href="/tags/mina/" style="font-size: 13.08px;">mina</a> <a href="/tags/mongodb/" style="font-size: 19.23px;">mongodb</a> <a href="/tags/mq/" style="font-size: 13.85px;">mq</a> <a href="/tags/mqtt/" style="font-size: 10.77px;">mqtt</a> <a href="/tags/mybatis/" style="font-size: 16.15px;">mybatis</a> <a href="/tags/netty/" style="font-size: 10px;">netty</a> <a href="/tags/nginx/" style="font-size: 11.54px;">nginx</a> <a href="/tags/nio/" style="font-size: 13.08px;">nio</a> <a href="/tags/nodejs/" style="font-size: 10px;">nodejs</a> <a href="/tags/postgresql/" style="font-size: 10.77px;">postgresql</a> <a href="/tags/protobuf/" style="font-size: 10px;">protobuf</a> <a href="/tags/proxy/" style="font-size: 10px;">proxy</a> <a href="/tags/push/" style="font-size: 10px;">push</a> <a href="/tags/rabbitmq/" style="font-size: 11.54px;">rabbitmq</a> <a href="/tags/redis/" style="font-size: 13.85px;">redis</a> <a href="/tags/rpc/" style="font-size: 19.23px;">rpc</a> <a href="/tags/sitemesh/" style="font-size: 10px;">sitemesh</a> <a href="/tags/sld/" style="font-size: 10px;">sld</a> <a href="/tags/slf4j/" style="font-size: 10px;">slf4j</a> <a href="/tags/socket/" style="font-size: 14.62px;">socket</a> <a href="/tags/spatialite-for-android/" style="font-size: 10px;">spatialite for android</a> <a href="/tags/spi/" style="font-size: 10px;">spi</a> <a href="/tags/spring/" style="font-size: 18.46px;">spring</a> <a href="/tags/tomcat/" style="font-size: 10.77px;">tomcat</a> <a href="/tags/transaction/" style="font-size: 10px;">transaction</a> <a href="/tags/web/" style="font-size: 10px;">web</a> <a href="/tags/xSocket/" style="font-size: 14.62px;">xSocket</a> <a href="/tags/zbus/" style="font-size: 10px;">zbus</a> <a href="/tags/zookeeper/" style="font-size: 10.77px;">zookeeper</a> <a href="/tags/数据结构/" style="font-size: 10.77px;">数据结构</a>
    </div>
  </div>

  
    
  <div class="widget-wrap">
    <h3 class="widget-title">Archives</h3>
    <div class="widget">
      <ul class="archive-list"><li class="archive-list-item"><a class="archive-list-link" href="/archives/2016/01/">January 2016</a><span class="archive-list-count">67</span></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2015/12/">December 2015</a><span class="archive-list-count">81</span></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2015/11/">November 2015</a><span class="archive-list-count">2</span></li></ul>
    </div>
  </div>

  
    
  <div class="widget-wrap">
    <h3 class="widget-title">Recents</h3>
    <div class="widget">
      <ul>
        
          <li>
            <a href="/2016/01/30/资料/">(no title)</a>
          </li>
        
          <li>
            <a href="/2016/01/30/java/13.Classloader系列一/">ClassLoader系列一</a>
          </li>
        
          <li>
            <a href="/2016/01/29/hadoop/1.hadoop系列之源码编译/">hadoop系列一之源码编译</a>
          </li>
        
          <li>
            <a href="/2016/01/29/go/1.GO系列之入门/">Go系列一之入门</a>
          </li>
        
          <li>
            <a href="/2016/01/28/tomcat/2.tomcat性能优化/">tomcat性能优化</a>
          </li>
        
      </ul>
    </div>
  </div>

  
</aside>
        
      </div>
      <footer id="footer">
  
  <div class="outer">
    <div id="footer-info" class="inner">
      &copy; 2016 John Doe<br>
      Powered by <a href="http://hexo.io/" target="_blank">Hexo</a>
    </div>
  </div>
</footer>
    </div>
    <nav id="mobile-nav">
  
    <a href="/" class="mobile-nav-link">Home</a>
  
    <a href="/archives" class="mobile-nav-link">Archives</a>
  
</nav>
    


<script src="http://libs.baidu.com/jquery/2.0.3/jquery.min.js"></script>


  <link rel="stylesheet" href="/fancybox/jquery.fancybox.css" type="text/css">
  <script src="/fancybox/jquery.fancybox.pack.js" type="text/javascript"></script>


<script src="/js/script.js" type="text/javascript"></script>

  </div>
</body>
</html>